Как я могу заставить ждать продолжить в том же потоке?

await не гарантирует продолжения той же задачи для порожденных задач:

private void TestButton_Click(object sender, RoutedEventArgs e)
{
    Task.Run(async () =>
    {
        Debug.WriteLine("running on task " + Task.CurrentId);
        await Task.Delay(TimeSpan.FromMilliseconds(100));
        Debug.WriteLine("running on task " + Task.CurrentId);
    });
}

Выход этого:

running on task 1
running on task

Таким образом, мы видим, что не только выполнение переместилось в другую задачу, но и в поток пользовательского интерфейса. Как я могу создать отдельную задачу и заставить ждать всегда продолжать эту задачу? Долгосрочные задачи также не делают этого.

Я видел несколько реализаций SynchronizationContext, но до сих пор ни одна из них не работала, в данном случае, потому что он использует потоки и System.Threading.Thread недоступен для uwp.

2 ответа

Решение

Таким образом, мы видим, что не только выполнение переместилось в другую задачу, но и в поток пользовательского интерфейса.

Нет, это не в потоке пользовательского интерфейса. Это просто технически не на задачу, либо. Я объясняю, почему это происходит в моем блоге на Task.CurrentId в async методы.

Как я могу создать отдельную задачу и заставить ждать всегда продолжать эту задачу? Долгосрочные задачи также не делают этого.

Вы на правильном пути: вам нужен кастом SynchronizationContext (или обычай TaskScheduler).

Я видел несколько реализаций SynchronizationContext, но до сих пор ни одна из них не работала, в данном случае, потому что он использует потоки и System.Threading.Thread недоступен для uwp.

Попробуйте мой. Должно работать на UWP 10.0.

Не использовать Task.Runпросто сделайте обработчик событий асинхронным

 private async void TestButton_Click(object sender, RoutedEventArgs e)
 {
     await dedicated();
 }

 private async Task dedicated()
 {
     Console.WriteLine("running on task {0}", Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null");
     await Task.Delay(TimeSpan.FromMilliseconds(100));
     Console.WriteLine("running on task {0}", Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null");
 }

Узнайте больше о Task.CurrentId в асинхронных методах здесь:

Так Task.CurrentId возвращается null потому что нет задачи на самом деле выполнения.

Ответить на комментарии

  1. "Он по-прежнему работает в потоке пользовательского интерфейса, а не порождается задачей".

Случай с потоком пула потоков включен в ссылку. В частности, посмотрите на этот пример

static void Main(string[] args)
{
    var task = Task.Run(() => MainAsync());
    task.Wait();
    taskRun = task.Id.ToString();

    Console.WriteLine(beforeYield + "," + afterYield + "," + taskRun);
    Console.ReadKey();
}

static async Task MainAsync()
{
    beforeYield = Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null";
    await Task.Yield();
    afterYield = Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null";
}

Опять же, разъяснение

null вступает в игру, потому что async Метод сначала выполняется как фактическая задача в пуле потоков. Однако после его await, он возобновляется как обычный делегат в пуле потоков (не фактическая задача).

  1. "Мой вопрос заключается в том, как остановить это от этого"

Это деталь реализации async позвоните, я могу только процитировать ссылку снова:

Вполне вероятно, что такое поведение является просто результатом самой простой и эффективной реализации.

Так что вы не можете и не должны мешать ему делать это, насколько это действительно async вызов обеспокоен.

То, что вы описываете как ожидаемое поведение, вместо этого эквивалентно Task.Run без await

 private void expected()
 {
     Task task = Task.Run(() =>
     {
         Console.WriteLine("Before - running on task {0} {1}", 
            Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
            Environment.CurrentManagedThreadId);
         Task.Delay(TimeSpan.FromMilliseconds(100)).Wait();
         Console.WriteLine("After - running on task {0} {1}", 
            Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
            Environment.CurrentManagedThreadId);
     });
 }

Или вложенный Task.Run

 private void expected()
 {
     Task task = Task.Run(() =>
     {
         Console.WriteLine("Before - running on task {0} {1}",
             Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
             Environment.CurrentManagedThreadId);
         var inner = Task.Run( async () =>
             await Task.Delay(TimeSpan.FromMilliseconds(100)));
         inner.Wait();
         Console.WriteLine("After - running on task {0} {1}",
             Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
             Environment.CurrentManagedThreadId);
     });
 }

Выход

Before - running on task 312 11
After - running on task 312 11
Before - running on task 360 11
After - running on task 360 11
Before - running on task 403 15
After - running on task 403 15
Другие вопросы по тегам