Как запустить Task после его выполнения?

Здравствуйте, столкнулся с проблемой, которую всё не как не могу решить:
Есть кнопка, при нажатии которой вызывается метод:
private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            OnlyLike();           
        }

В методе вызывается поток Task, по завершению которого Добавляется ответ в TextBox.Text
public void OnlyLike()
        {
            Task<string> LikeTurbo = VK.NakrytkaLike(KeyAntigateTextBox.Text);
            LikeTurbo.ContinueWith(task =>
            {
                TextBox.Text += LikeTurbo.Result.ToString();
            }, TaskScheduler.FromCurrentSynchronizationContext());
        }

Вопрос, как сделать так, Чтоб по нажатию кнопки происходило выполнение Метода OnlyLike несколько раз (к примеру 5), НО чтоб метод этот начинался повторно выполнятся только после завершения потока Task LikeTurbo
  • Вопрос задан
  • 3663 просмотра
Решения вопроса 1
@mayorovp
Я немного не понял, вам надо запускать задачи последовательно или параллельно? А если параллельно - то обрабатывать результаты сразу или вместе?

1. Последовательный запуск:
private async void OnlyLike()
{
    for (var i=0; i<5; i++)
        TextBox.Text += await VK.NakrytkaLike(KeyAntigateTextBox.Text);
}


2. Параллельный запуск, обработка всех результатов вместе
private async void OnlyLike()
{
    var tasks = Enumerable.Range(0, 5).Select(i => VK.NakrytkaLike(KeyAntigateTextBox.Text));
    foreach (var result in await Task.WhenAll(tasks))
        TextBox.Text += result;
}


3. Параллельный запуск, обработка результатов сразу же:
private async void OnlyLike()
{
    TextBox.Text += await VK.NakrytkaLike(KeyAntigateTextBox.Text);
}

private void StartButton_Click(object sender, RoutedEventArgs e)
{
    for (var i=0; i<5; i++)
        OnlyLike();
}


Выбирайте что нравится. И не бойтесь использовать async/await - оно создано для упрощения кода, а не для усложнения :)

UPD

Последовательный запуск на фреймворке 4.0:
public void OnlyLike()
{
    var scheduler = TaskScheduler.Current ?? TaskScheduler.FromCurrentSynchronizationContext();
    Task task = TaskEx.FromResult(false);
    var text = KeyAntigateTextBox.Text;
    for (var i=0; i<5; i++) {
        task = task.ContinueWith(_ => {
            var LikeTurbo = VK.NakrytkaLike(text);
            LikeTurbo.ContinueWith(_ => {
                 TextBox.Text += LikeTurbo.Result;
            }, scheduler);
            return LikeTurbo;
        }, TaskContinuationOptions.ExecuteSynchronously).Unwrap();
    }
}

...

public static class TaskEx {
    public static Task<T> FromResult<T> (T result) {
        var tcs = new TaskCompletionSource<T>();
        tcs.SetResult(result);
        return tcs.Task;
    }

    public static Task<T> Unwrap<T>(this Task<Task<T>> task) {
        var tcs = new TaskCompletionSource<T>();
        task.ContinueWith(_ => {
            if (task.IsCanceled) tcs.SetCancelled();
            else if (task.IsFaulted) tcs.SetException(task.Exception);
            else task.Result.ContinueWith(innerTask => {
                if (innerTask.IsCanceled) tcs.SetCancelled();
                else if (innerTask.IsFaulted) tcs.SetException(task.Exception);
                else tcs.SetResult(innerTask.Result);
            }, TaskContinuationOptions.ExecuteSynchronously);
        }, TaskContinuationOptions.ExecuteSynchronously);
        return tcs.Task;
    }
}


Выглядит многословно, но на самом деле - ничего сложного. В цикле задачи ставятся в очередь одна за другой при помощи конструкции task = task.ContinueWith(...).Unwrap(). Здесь Unwrap() - это функция, позволяющая дождаться выполнения дочерней задачи, фактически простой распаковщик монады Task Task T -> Task T.

Внутри такого цикла находится, фактически, старый код метода OnlyLike() - за тем исключением, что теперь он еще и возвращает ту задачу, которую создал (это нужно, чтобы дождаться ее выполнения). Здесь я позволил себе небольшое ускорение - поскольку я написал return LikeTurbo - каждая следующая итерация цикла будет ждать лишь предыдущей задачи VK.NakrytkaLike - но не будет ждать вывода на экран предыдущих результатов. Если важно именно дождаться вывода результатов - то надо возвращать не LikeTurbo, а результат ContinueWith.

Также тут очень важно расположение вызова TaskScheduler.FromCurrentSynchronizationContext(); Поскольку в момент исполнения прошлой задачи мы можем находиться в любом контексте - то ориентироваться на текущий контекст нельзя. Нужный нам планировщик задач следует сохранить при входе в метод (на будущее: его нужно _всегда_ сохранять _только_ при входе в метод!)
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
Neuroware
@Neuroware
Программист в свободное от работы время
Проблема тут скорее архитектурная, если требуется подобное, значит чтото здесь не так, ну а вообще ничего не мешает обернуть это все в отдельный Task или Thread и в нем последовательно вызывать таски столько раз сколько захочется.
Ответ написан
Комментировать
@1hroft1
for(var i=1; i<=5; i++)
{
    Console.WriteLine("Сейчас будем запускать продолжающиеся потоки в " + i + " раз");
    Task<string> LikeTurbo = Task.Factory.StartNew<string>(()=>
    {
        Console.WriteLine("Выполнение в первом потоке №"+i);
        return "somestring";
    });
    Task cont_task=LikeTurbo.ContinueWith(task =>
    {
        Console.WriteLine("Выполнение во втором потоке №" + i);
    });
     cont_task.Wait();
}


UPD. И вот вам полезности по таскам dotnetcodr.com/task-parallel-library
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы