Задача кодирования для запуска в определенном потоке
Я обновил старый проект, который использовал 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.