2013-09-25 3 views
0

я написал свой собственный TaskScheduler:Почему задачи выполняются в основном потоке?

public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler 
{ 

    private BlockingCollection<Task> _tasks = new BlockingCollection<Task>(); 
    private List<Thread> _threads; 
    private bool work = true; 

    public LimitedConcurrencyLevelTaskScheduler(int maxConcurrencyLevel) 
    { 

     _threads = new List<Thread>(); 
     for (int i = 0; i < maxConcurrencyLevel; i++) 
     { 
      _threads.Add(new Thread(() => 
             { 
              while (work) 
              { 
               TryExecuteTask(_tasks.Take()); 
              } 
             }) { IsBackground = true, Name = "TaskShedulerThread#" + i }); 
     } 
     foreach (var thread in _threads) 
     { 
      thread.Start(); 
     } 

    } 

    protected override void QueueTask(Task task) 
    { 
     _tasks.Add(task); 
    } 

    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) 
    { 
     return !_threads.Contains(Thread.CurrentThread) && TryExecuteTask(task); 
    } 

    public override int MaximumConcurrencyLevel { get { return 1; } } 

    protected override IEnumerable<Task> GetScheduledTasks() 
    { 
     return _tasks.ToArray(); 
    } 

    public void Dispose() 
    { 
     if (_threads != null) 
     { 
      _tasks.CompleteAdding(); 
      work = false; 

      _tasks.Dispose(); 
      _tasks = null; 
      _threads = null; 
     } 
    } 
} 

и использовать его таким образом:

static void Main(string[] args) 
    { 
     var taskScheduller = new LimitedConcurrencyLevelTaskScheduler(1); 
     Thread.CurrentThread.Name = "MainThread"; 
     var taskFactory = new TaskFactory(taskScheduller); 
     var tasks = new Task[100]; 
     for (int i = 0; i < 100; i++) 
     { 
      tasks[i] = taskFactory.StartNew(() => Console.WriteLine(String.Format("Call in {0}", Thread.CurrentThread.Name))); 
     } 
     Task.WaitAll(tasks); 
    } 

выход программки по:

Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in MainThread 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in MainThread 
Call in MainThread 
Call in MainThread 
Call in MainThread 
Call in MainThread 
Call in MainThread 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
Call in TaskShedulerThread#0 
... 

Почему мои задачи запускались в том главном потоке?

ответ

0

Задачи выполняются на основной резьбе из-за функции TPL, известной как task inlining. Когда поток вызывает WaitAll (или любой аналогичный метод) для задачи, которая еще не приступила к выполнению, TPL может разрешить упомянутому потоку попытаться выполнить самую ожидающую задачу, а не блокировать до тех пор, пока она не будет выполнена одним из рабочих планировщика задач потоки.

Управление этой поведением можно контролировать, переопределив метод планировщика заданий TryExecuteTaskInline, запрещающий выполнение каких-либо задач, если текущий поток не является рабочим потоком.

protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) 
{ 
    return !_threads.Contains(Thread.CurrentThread) && TryExecuteTask(task); 
} 

В вашей реализации, вам просто нужно удалить отрицание до Contains проверки:

protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) 
{ 
    return _threads.Contains(Thread.CurrentThread) && TryExecuteTask(task); 
} 
Смежные вопросы