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
...

タスクがメイン スレッドで実行されるのはなぜですか?

4

1 に答える 1

0

タスクのインライン化と呼ばれる TPL 機能により、タスクはメイン スレッドで実行されます。まだ実行を開始していないタスクでスレッドが (または同様のメソッドを) 呼び出すとWaitAll、TPL は、タスク スケジューラのワーカー スレッドの 1 つによって実行されるまでブロックするのではなく、そのスレッドが保留中のタスク自体を実行しようとすることを許可する場合があります。 .

この動作は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);
}
于 2013-09-25T10:11:07.300 に答える