2

我有一个类型的静态字段ConcurrentQueue

static readonly ConcurrentQueue<int> q = new ConcurrentQueue<int>();

和一个异步方法:

static async Task<int?> NextNum()
{
    int? n = await Task.Run<int?>(() =>
    {
        int i = 0;

        if (q.TryDequeue(out i)) return i;
        return null;
    });

    return n;
}

然后我执行这段代码:

var nt = NextNum();
q.Enqueue(10);

nt.Wait();
Console.WriteLine("{0}", nt.Result.HasValue ? nt.Result.Value : -1);

输出是10.

现在我将MethodImpl属性添加到我的异步方法:

[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
static async Task<int?> NextNum()
{
    int? n = await Task.Run<int?>(() =>
    {
        int i = 0;

        if (q.TryDequeue(out i)) return i;
        return null;
    });

    return n;
}

当我执行前面提到的代码时,我得到-1.

问题:这是否意味着在异步方法中返回的任务不会立即启动?如果我们添加MethodImpl(with AggressiveInlining) 属性,它会立即启动吗?

我想知道用 AggressiveInlining 修饰的方法是否对任务调度程序行为有任何影响。

4

1 回答 1

2

您的测试是不确定的,因此结果可能会根据时间/线程切换/机器负载/内核数/等的变化而有所不同。

例如,如果您将测试更改为:

var nt = NextNum();
Thread.Sleep(1000);
q.Enqueue(10);

那么输出很可能-1即使没有AggressiveInlining.

问题:这是否意味着在异步方法中返回的任务不会立即启动?如果我们添加 MethodImpl(带有 AggressiveInlining)属性,它会立即启动吗?

一点也不。NextNumalways返回的任务立即开始。但是,排队到线程池的任务Task.Run可能不会。这就是您看到行为差异的地方。

在您的原始测试中,排队的任务Task.Run恰好需要足够长的时间q.Enqueue才能在它之前执行。在您的第二个测试中,排队的任务Task.Run恰好在q.Enqueue. 两者都是不确定的,AggressiveInlining只是改变了时间。

评论更新:

我想知道用 AggressiveInlining 修饰的方法是否对任务调度程序行为有任何影响。

不,不是的。

于 2013-05-12T12:42:52.593 回答