Горячий для продолжения в настраиваемом планировщике задач после ожидания задачи? [Дубликат]

Убедитесь, что внешние ключи не указаны как уникальные в родительском. У меня была такая же проблема, и я решил ее, демаркируя ее как не уникальную.

33
задан Stephane Delcroix 15 March 2013 в 11:20
поделиться

4 ответа

После комментариев похоже, что вы хотите управлять планировщиком, на котором запускается код после ожидания.

Компиляция создает продолжение с ожиданием, которое выполняется по текущему Синхронизационному окну по умолчанию. Поэтому лучше всего настроить SynchronizationContext перед вызовом.

Есть несколько способов подождать определенного контекста. См. Настроить ожидание от Jon Skeet, особенно часть SwitchTo, для получения дополнительной информации о том, как реализовать что-то вроде этого.

EDIT: метод SwitchTo из TaskEx удален, так как это было слишком легко злоупотреблять. См. форум MSDN по причинам.

2
ответ дан Andrew Wilkinson 26 August 2018 в 21:52
поделиться

TaskCompletionSource<T>.Task сконструирован без какого-либо действия, и планировщик назначается при первом вызове ContinueWith(...) (из асинхронного программирования с реактивной платформой и параллельной библиотекой задач - часть 3 ).

К счастью, вы можете немного изменить поведение ожидания, выполнив собственный класс, полученный из INotifyCompletion, а затем используя его в шаблоне, подобном await SomeTask.ConfigureAwait(false), чтобы настроить планировщик, который задача должна начать использовать в OnCompleted(Action continuation) (из ждать чего-нибудь; ).

Вот использование:

    TaskCompletionSource<object> source = new TaskCompletionSource<object>();

    public async Task Foo() {
        // Force await to schedule the task on the supplied scheduler
        await SomeAsyncTask().ConfigureScheduler(scheduler);
    }

    public Task SomeAsyncTask() { return source.Task; }

Вот простая реализация ConfigureScheduler с использованием Метод расширения задачи с важной частью в OnCompleted:

public static class TaskExtension {
    public static CustomTaskAwaitable ConfigureScheduler(this Task task, TaskScheduler scheduler) {
        return new CustomTaskAwaitable(task, scheduler);
    }
}

public struct CustomTaskAwaitable {
    CustomTaskAwaiter awaitable;

    public CustomTaskAwaitable(Task task, TaskScheduler scheduler) {
        awaitable = new CustomTaskAwaiter(task, scheduler);
    }

    public CustomTaskAwaiter GetAwaiter() { return awaitable; }

    public struct CustomTaskAwaiter : INotifyCompletion {
        Task task;
        TaskScheduler scheduler;

        public CustomTaskAwaiter(Task task, TaskScheduler scheduler) {
            this.task = task;
            this.scheduler = scheduler;
        }

        public void OnCompleted(Action continuation) {
            // ContinueWith sets the scheduler to use for the continuation action
            task.ContinueWith(x => continuation(), scheduler);
        }

        public bool IsCompleted { get { return task.IsCompleted; } }
        public void GetResult() { }
    }
}

Вот рабочий пример, который будет компилироваться в качестве консольного приложения:

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace Example {
    class Program {
        static TaskCompletionSource<object> source = new TaskCompletionSource<object>();
        static TaskScheduler scheduler = new CustomTaskScheduler();

        static void Main(string[] args) {
            Console.WriteLine("Main Started");
            var task = Foo();
            Console.WriteLine("Main Continue ");
            // Continue Foo() using CustomTaskScheduler
            source.SetResult(null);
            Console.WriteLine("Main Finished");
        }

        public static async Task Foo() {
            Console.WriteLine("Foo Started");
            // Force await to schedule the task on the supplied scheduler
            await SomeAsyncTask().ConfigureScheduler(scheduler);
            Console.WriteLine("Foo Finished");
        }

        public static Task SomeAsyncTask() { return source.Task; }
    }

    public struct CustomTaskAwaitable {
        CustomTaskAwaiter awaitable;

        public CustomTaskAwaitable(Task task, TaskScheduler scheduler) {
            awaitable = new CustomTaskAwaiter(task, scheduler);
        }

        public CustomTaskAwaiter GetAwaiter() { return awaitable; }

        public struct CustomTaskAwaiter : INotifyCompletion {
            Task task;
            TaskScheduler scheduler;

            public CustomTaskAwaiter(Task task, TaskScheduler scheduler) {
                this.task = task;
                this.scheduler = scheduler;
            }

            public void OnCompleted(Action continuation) {
                // ContinueWith sets the scheduler to use for the continuation action
                task.ContinueWith(x => continuation(), scheduler);
            }

            public bool IsCompleted { get { return task.IsCompleted; } }
            public void GetResult() { }
        }
    }

    public static class TaskExtension {
        public static CustomTaskAwaitable ConfigureScheduler(this Task task, TaskScheduler scheduler) {
            return new CustomTaskAwaitable(task, scheduler);
        }
    }

    public class CustomTaskScheduler : TaskScheduler {
        protected override IEnumerable<Task> GetScheduledTasks() { yield break; }
        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return false; }
        protected override void QueueTask(Task task) {
            TryExecuteTask(task);
        }
    }
}
8
ответ дан kkm 26 August 2018 в 21:52
поделиться

Можете ли вы поместиться для вызова этого метода:

  await Task.Factory.StartNew(
        () => { /* to do what you need */ }, 
        CancellationToken.None, /* you can change as you need */
        TaskCreationOptions.None, /* you can change as you need */
        customScheduler);
2
ответ дан Papay 26 August 2018 в 21:52
поделиться

Я думаю, что вы действительно хотите сделать Task.Run, но с помощью специального планировщика. StartNew не работает интуитивно с асинхронными методами; Стивен Тууб имеет отличное сообщение в блоге о различиях между Task.Run и TaskFactory.StartNew .

Итак, чтобы создать свой собственный Run, вы можете сделать что-то вроде этого :

private static readonly TaskFactory myTaskFactory = new TaskFactory(
    CancellationToken.None, TaskCreationOptions.DenyChildAttach,
    TaskContinuationOptions.None, new MyTaskScheduler());
private static Task RunOnMyScheduler(Func<Task> func)
{
  return myTaskFactory.StartNew(func).Unwrap();
}
private static Task<T> RunOnMyScheduler<T>(Func<Task<T>> func)
{
  return myTaskFactory.StartNew(func).Unwrap();
}
private static Task RunOnMyScheduler(Action func)
{
  return myTaskFactory.StartNew(func);
}
private static Task<T> RunOnMyScheduler<T>(Func<T> func)
{
  return myTaskFactory.StartNew(func);
}

Затем вы можете выполнить синхронные асинхронные методы или в своем настраиваемом планировщике.

35
ответ дан Stephen Cleary 26 August 2018 в 21:52
поделиться
Другие вопросы по тегам:

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