Задача кодирования для запуска в определенном потоке

Я обновил старый проект, который использовал Supersocket для подключения к старым серверам C++. В последней версии (от 0.7.0 => 0.8.0.8) я получил исключение en при попытке переподключения (он говорит, что сокет был открыт в другом потоке) Я хотел бы иметь класс, который ставит задачи в очередь (Первое соединение и переподключение) и запускает их на специальную тему.

Я видел этот подход, но когда я пытаюсь запустить задачу, созданную как получил исключение

ExecuteTask may not be called for a task which was previously queued to a different TaskScheduler.

Вот класс, который я взял по ссылке выше

 public class SameThreadTaskScheduler : TaskScheduler, IDisposable
{
    #region publics
    public SameThreadTaskScheduler(string name)
    {
        scheduledTasks = new Queue<Task>();
        threadName = name;
    }
    public override int MaximumConcurrencyLevel => 1;

    public void Dispose()
    {
        lock (scheduledTasks)
        {
            quit = true;
            Monitor.PulseAll(scheduledTasks);
        }
    }
    #endregion

    #region protected overrides
    protected override IEnumerable<System.Threading.Tasks.Task> GetScheduledTasks()
    {
        lock (scheduledTasks)
        {
            return scheduledTasks.ToList();
        }
    }

    protected override void QueueTask(Task task)
    {
        if (myThread == null)
            myThread = StartThread(threadName);
        if (!myThread.IsAlive)
            throw new ObjectDisposedException("My thread is not alive, so this object has been disposed!");
        lock (scheduledTasks)
        {
            scheduledTasks.Enqueue(task);
            Monitor.PulseAll(scheduledTasks);
        }
    }

    public void Queue(Task task)
    {
        QueueTask(task);
    }


    protected override bool TryExecuteTaskInline(Task task, bool task_was_previously_queued)
    {
        return false;
    }
    #endregion

    private readonly Queue<System.Threading.Tasks.Task> scheduledTasks;
    private Thread myThread;
    private readonly string threadName;
    private bool quit;

    private Thread StartThread(string name)
    {
        var t = new Thread(MyThread) { Name = name };
        using (var start = new Barrier(2))
        {
            t.Start(start);
            ReachBarrier(start);
        }
        return t;
    }
    private void MyThread(object o)
    {
        Task tsk;
        lock (scheduledTasks)
        {
            //When reaches the barrier, we know it holds the lock.
            //
            //So there is no Pulse call can trigger until
            //this thread starts to wait for signals.
            //
            //It is important not to call StartThread within a lock.
            //Otherwise, deadlock!
            ReachBarrier(o as Barrier);
            tsk = WaitAndDequeueTask();
        }
        for (;;)
        {
            if (tsk == null)
                break;
            TryExecuteTask(tsk);
            lock (scheduledTasks)
            {
                tsk = WaitAndDequeueTask();
            }
        }
    }
    private Task WaitAndDequeueTask()
    {
        while (!scheduledTasks.Any() && !quit)
            Monitor.Wait(scheduledTasks);
        return quit ? null : scheduledTasks.Dequeue();
    }

    private static void ReachBarrier(Barrier b)
    {
        if (b != null)
            b.SignalAndWait();
    }
}

Вот как я называю задачу

 public void RegisterServers()
    {

        sameThreadTaskScheduler.Queue(new Task(() =>
            {
               ...something
            }));

Что я делаю не так? Спасибо

1 ответ

Вы должны начать задачу, чтобы связать ее с TaskScheduler, В своем коде вы ставите задачу в очередь вручную, выполняя ее наоборот, чтобы задача не привязывалась к вашему планировщику задач (или вообще к нему), и TryExecuteTask потерпит неудачу с этой ошибкой. Описание довольно загадочное, как любое актуальное TaskScheduler отличается от null,

Для задачи, которую вы создали без выполнения, существуют перегрузки Task.RunSynchronously а также Task.Start что взять TaskScheduler,

Для задачи, которая запускается автоматически, существуют перегрузки TaskFactory.StartNew а также TaskFactory<TResult>.StartNew что взять TaskScheduler,

Для задач продолжения существуют перегрузки Task.ContinueWith, Task<TResult>.ContinueWith, TaskFactory.ContinueWhenAll а также TaskFactory.ContinueWhenAny что взять TaskScheduler,

Перегрузки, которые не принимают планировщик задач, эквивалентны указанию TaskScheduler.Current, В случае TaskFactoryэто верно по умолчанию Task.Factory или один, созданный без планировщика задач во время вызова метода фабрики, в противном случае используется планировщик задач фабрики.


Контраст с перегрузками новее Task.Run, которые всегда используют TaskScheduler.Default, По мнению большинства опытных людей, планировщик пула потоков требуется чаще по умолчанию, чем TaskScheduler.Current который может быть связан с потоками, но было слишком поздно менять контракт для существующих API.

Другие вопросы по тегам