我正在尝试创建一个TaskScheduler,以限制可以同时运行的线程数。我正在使用this example。问题是我看到了一些我不理解的行为。

如果我像示例一样创建类:

LimitedConcurrencyLevelTaskScheduler lcts = new LimitedConcurrencyLevelTaskScheduler(5);
TaskFactory factory = new TaskFactory(lcts);


然后像这样运行它:

foreach (int i = 0; i < 10; ++i)
{
    factory.StartNew(() => DoWork());
}


做工作看起来像这样:

private async Task DoWork()
{
    // Do some stuff here like
    StaticIntValueWorkOne++;

    // And then more stuff that is async here
    int someValue = await DoAdditionalWorkAsync();
    Thread.Sleep(10000);
    StaticIntValueWorkTwo++;
}


我看到的是StaticIntValueWorkOne立即增加10次,而StaticIntValueWorkTwo仅增加一次。然后10秒钟后,我看到StaticIntValueWorkTwo递增,然后每10秒钟递增一次。我没有得到的是DoAdditionalWorkAsync()上的并发性。我以为我会看到StaticIntValueWorkOne增加一次,然后StaticIntValueWorkTwo增加一次。我想念什么?我只需要await上的factor.StartNew()吗?

最佳答案

我正在尝试创建一个TaskScheduler,以限制可以同时运行的线程数。


您可能要skip straight to the answer。 ;)

var scheduler = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 5)
    .ConcurrentScheduler;



  我没有得到的是DoAdditionalWorkAsync()上的并发性正在做什么。


任务计划程序仅适用于执行代码。当async方法在任务计划程序上执行时,您可以认为它被分解为多个任务,每个await点都有一个中断。默认情况下,在await之后,async方法将重新输入其任务计划程序。 async方法不在await时在任务计划程序中。

因此,在await ing方法期间,调度限制(一次5个)根本不适用。因此,在您的DoWork中,该方法将首先递增变量,然后屈服于任务计划程序。产生收益时,它不会“计入”您的并发限制。稍后,当该方法恢复时,它将阻塞线程(执行“计数”)并增加第二个变量。

使用此代码:

private static void Main(string[] args)
{
    var scheduler = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 5)
        .ConcurrentScheduler;
    TaskFactory factory = new TaskFactory(scheduler);

    for (int i = 0; i < 10; ++i)
    {
        factory.StartNew(() => DoWork());
    }

    Console.ReadKey();
}

private static int StaticIntValueWorkOne, StaticIntValueWorkTwo;

private static async Task DoWork()
{
    // Do some stuff here like
    Console.WriteLine(DateTime.UtcNow + " StaticIntValueWorkOne" + Interlocked.Increment(ref StaticIntValueWorkOne));

    // And then more stuff that is async here
    await Task.Yield();
    Thread.Sleep(10000);
    Console.WriteLine(DateTime.UtcNow + " StaticIntValueWorkTwo" + Interlocked.Increment(ref StaticIntValueWorkTwo));
}


我得到此(预期)输出:

3/20/2015 11:01:53 AM StaticIntValueWorkOne1
3/20/2015 11:01:53 AM StaticIntValueWorkOne5
3/20/2015 11:01:53 AM StaticIntValueWorkOne4
3/20/2015 11:01:53 AM StaticIntValueWorkOne2
3/20/2015 11:01:53 AM StaticIntValueWorkOne3
3/20/2015 11:01:53 AM StaticIntValueWorkOne6
3/20/2015 11:01:53 AM StaticIntValueWorkOne9
3/20/2015 11:01:53 AM StaticIntValueWorkOne10
3/20/2015 11:01:53 AM StaticIntValueWorkOne7
3/20/2015 11:01:53 AM StaticIntValueWorkOne8
3/20/2015 11:02:03 AM StaticIntValueWorkTwo1
3/20/2015 11:02:03 AM StaticIntValueWorkTwo3
3/20/2015 11:02:03 AM StaticIntValueWorkTwo2
3/20/2015 11:02:03 AM StaticIntValueWorkTwo4
3/20/2015 11:02:03 AM StaticIntValueWorkTwo5
3/20/2015 11:02:13 AM StaticIntValueWorkTwo6
3/20/2015 11:02:13 AM StaticIntValueWorkTwo7
3/20/2015 11:02:13 AM StaticIntValueWorkTwo8
3/20/2015 11:02:13 AM StaticIntValueWorkTwo9
3/20/2015 11:02:13 AM StaticIntValueWorkTwo10


如果要限制异步代码的并发性,请查看SemaphoreSlim或TPL Dataflow。

关于c# - 等待和任务并发,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29159866/

10-17 00:55