任务<IEnumerable<T>>线程安全问题

问题描述 投票:0回答:1

我正在任务中创建所有变量,但没有看到任何共享状态。在我拨打电话的其中一个实例中,其中一个

MyData
值(例如:
myData.Chocolate
)为空。我拨打电话时没有日志,无法复制它。我不确定外部调用是否返回 null,或者是否存在任何线程问题导致该字段在特定实例期间为 null。

您在下面的程序中看到任何线程安全问题吗?

public async Task<IEnumerable<MyData>> MyMethodAsync(IEnumerable<string> data)
{
    var tasks = data.Select(async d =>
    {
        MyData myData = new MyData();

        //API call
        CandyData candyData = // await async API call to Graph Client

        myData.Chocolate = candyData.Chocolate;
        myData.Toffee = candyData.Toffee;
        // more similar setters

        return myData;
    });
    var results = await Task.WhenAll(tasks);
    return results;
}
c# multithreading thread-safety
1个回答
0
投票

你的代码看起来不错。

我用以下方法测试了它:

async Task Main()
{
    var results = await MyMethodAsync(new [] { "A", "B", "C", });
    foreach (var result in results)
        Console.WriteLine(result.Chocolate);
}

public async Task<IEnumerable<MyData>> MyMethodAsync(IEnumerable<string> data)
{
    var tasks = data.Select(async d =>
    {
        MyData myData = new MyData();

        CandyData candyData = await GetCandyDataAsync(d);

        myData.Chocolate = candyData.Chocolate;
        myData.Toffee = candyData.Toffee;

        return myData;
    });
    var results = await Task.WhenAll(tasks);
    return results;
}

public Task<CandyData> GetCandyDataAsync(string data) =>
    Task.Run(() =>
    {
        Thread.Sleep(TimeSpan.FromSeconds(10.0));
        return new CandyData()
        {
            Chocolate = data,
            Toffee = data
        };
    });

正如预期的那样,10 秒后控制台上产生了

A
B
C

这意味着任何问题都可能出在您实施

GetCandyDataAsync
的过程中。

我的下一个想法是尝试清理代码,使其更易于维护和测试。

所以,我根据你的

MyMethodAsync
写了这个扩展方法:

public static async Task<IEnumerable<R>> SelectMany<T, U, R>(
        this IEnumerable<T> source,
        Func<T, Task<U>> taskSelector,
        Func<T, U, R> resultSelector) =>
    await Task.WhenAll(
        source
            .Select(async t =>
            {
                U u = await taskSelector(t);
                R r = resultSelector(t, u);
                return r;
            }));

是的,您的代码实际上封装在

SelectMany
中,现在可以用来编写
MyMethodAsync
方法的简单 LINQ 查询实现。

这是:

public async Task<IEnumerable<MyData>> MyMethodAsync(IEnumerable<string> data) =>
    await
        from d in data
        from candyData in GetCandyDataAsync(d)
        select new MyData()
        {
            Chocolate = candyData.Chocolate,
            Toffee = candyData.Toffee,
        };

同样,这会产生与上面相同的结果,但它将艰苦的工作封装在一个方便的扩展方法中。

我在发布之前进行了全面测试。

© www.soinside.com 2019 - 2024. All rights reserved.