Разное поведение между Factory.StartNew и Task.Run?C#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Разное поведение между Factory.StartNew и Task.Run?

Сообщение Anonymous »

Я пытаюсь понять разницу между Factory.StartNew и Task.Run. Я видел эквивалентность в разных местах, например здесь.

Думаю, в моем случае мне придется использовать Factory.StartNew(), так как я хочу подключить свой собственный TaskScheduler.< /p>

Подводя итог, кажется, что:

Код: Выделить всё

Task.Run(action)
Строго эквивалентно:

Код: Выделить всё

Task.Factory.StartNew(action,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
Но я провел несколько тестов с помощью простой SerialQueue, взятой из образцов Microsoft для параллельного программирования с .NET Framework.

Вот простой код:

Код: Выделить всё

/// Represents a queue of tasks to be started and executed serially.
public class SerialTaskQueue
{
/// The ordered queue of tasks to be executed.  Also serves as a lock protecting all shared state.
private Queue _tasks = new Queue();
/// The task currently executing, or null if there is none.
private Task _taskInFlight;

/// Enqueues the task to be processed serially and in order.
/// 
The function that generates a non-started task.
public void Enqueue(Func taskGenerator) { EnqueueInternal(taskGenerator); }

/// Enqueues the task to be processed serially and in order.
/// The task or functino that generates a task.
/// The task must not be started and must only be started by this instance.
private void EnqueueInternal(object taskOrFunction)
{
// Validate the task
if (taskOrFunction == null) throw new ArgumentNullException("task");
lock (_tasks)
{
// If there is currently no task in flight, we'll start this one
if (_taskInFlight == null) StartTask_CallUnderLock(taskOrFunction);
// Otherwise, just queue the task to be started later
else _tasks.Enqueue(taskOrFunction);
}
}

/// Starts the provided task (or function that returns a task).
/// The next task or function that returns a task.
private void StartTask_CallUnderLock(object nextItem)
{
Task next = nextItem as Task;
if (next == null) next = ((Func)nextItem)();

if (next.Status == TaskStatus.Created) next.Start();

_taskInFlight = next;
next.ContinueWith(OnTaskCompletion);
}

/// Called when a Task completes to potentially start the next in the queue.
/// The task that completed.
private void OnTaskCompletion(Task ignored)
{
lock (_tasks)
{
// The task completed, so nothing is currently in flight.
// If there are any tasks in the queue, start the next one.
_taskInFlight = null;
if (_tasks.Count > 0) StartTask_CallUnderLock(_tasks.Dequeue());
}
}
}
А теперь мой код некоторой моделируемой составленной задачи (включая ожидание/продолжение).

Код: Выделить всё

    public static async Task SimulateTaskSequence(int taskId)
{
Console.WriteLine("Task{0} - Start working 1sec (ManagedThreadId={1} IsThreadPoolThread={2})", taskId, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(200);

Console.WriteLine("Task{0} - Zzz 1st 1sec (ManagedThreadId={1} IsThreadPoolThread={2})", taskId, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
await Task.Delay(200);

Console.WriteLine("Task{0} - Done (ManagedThreadId={1} IsThreadPoolThread={2})", taskId, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
}
Тест1: использование очереди с Task.Run():

Код: Выделить всё

static void Main(string[] args)
{
Console.WriteLine($"Starting test program (ManagedThreadId={Thread.CurrentThread.ManagedThreadId} IsThreadPoolThread={Thread.CurrentThread.IsThreadPoolThread})");

SerialTaskQueue co_pQueue = new SerialTaskQueue();

for (int i = 0; i < 2; i++)
{
var local = i;
co_pQueue.Enqueue(() => Task.Run(() => { return SimulateTaskSequence(local);  }));
}
}
И результат правильный, очередь обрабатывается в ожидаемом порядке (достижение Task0 перед переключением на Task1).


Запуск тестовой программы (ManagedThreadId=1 IsThreadPoolThread=False)

Task0 – начало работы 1 с (ManagedThreadId=5 IsThreadPoolThread=True)

Task0 – Zzz 1-я 1 секунда (ManagedThreadId=5 IsThreadPoolThread=True)

Task0 – Готово (ManagedThreadId=5 IsThreadPoolThread=True)

Task1 – Начало работы 1 секунда (ManagedThreadId=5 IsThreadPoolThread) =True)

Task1 — Zzz 1st 1sec (ManagedThreadId=5 IsThreadPoolThread=True)

Task1 — Done (ManagedThreadId=8 IsThreadPoolThread=True)


Тест 2: просто использование Factory.StartNew с его полной эквивалентностью:

Код: Выделить всё

static void Main(string[] args)
{
Console.WriteLine($"Starting test program (ManagedThreadId={Thread.CurrentThread.ManagedThreadId} IsThreadPoolThread={Thread.CurrentThread.IsThreadPoolThread})");

SerialTaskQueue co_pQueue = new SerialTaskQueue();

for (int i = 0; i < 2; i++)
{
var local = i;
co_pQueue.Enqueue(() => Task.Factory.StartNew(() => { return SimulateTaskSequence(local); }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
}
}
Но на этот раз я получаю следующий результат:


Запуск тестовой программы ( ManagedThreadId=1 IsThreadPoolThread=False)

Task0 – начало работы 1 с (ManagedThreadId=5 IsThreadPoolThread=True)

Task0 – Zzz 1 секунда (ManagedThreadId=5 IsThreadPoolThread=True)
Задача 1 — начало работы 1 секунда (ManagedThreadId=5 IsThreadPoolThread=True) ЧТО?

Задача 1 — Zzz 1-я 1 секунда (ManagedThreadId=5 IsThreadPoolThread=True)

Задача 0 — выполнено (ManagedThreadId=9 IsThreadPoolThread=True)

Задача 1 — выполнено (ManagedThreadId=5 IsThreadPoolThread=True)


Я не понимаю разницы. Почему поведение другое? Я думал это эквивалентно?! (помните, что следующий шаг — подключение моего собственного планировщика)

Подробнее здесь: https://stackoverflow.com/questions/474 ... d-task-run
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • Уменьшение накладных расходов на замыкание в Task.Run/Factory.StartNew с помощью предопределенного объекта.
    Anonymous » » в форуме C#
    0 Ответы
    47 Просмотры
    Последнее сообщение Anonymous
  • Почему вызов неоднозначен между Task.Run(Func) и Task.Run(Func) при передаче имени метода
    Anonymous » » в форуме C#
    0 Ответы
    95 Просмотры
    Последнее сообщение Anonymous
  • Разница в производительности между ThreadPool.QueueUserWorkItem и Task.Factory.StartNew
    Anonymous » » в форуме C#
    0 Ответы
    17 Просмотры
    Последнее сообщение Anonymous
  • Многопоточный Task.factory.startnew
    Anonymous » » в форуме C#
    0 Ответы
    13 Просмотры
    Последнее сообщение Anonymous
  • Многопоточный Task.Factory.StartNew вызывает утечку памяти [закрыто]
    Anonymous » » в форуме C#
    0 Ответы
    15 Просмотры
    Последнее сообщение Anonymous

Вернуться в «C#»