我正在任务中创建所有变量,但没有看到任何共享状态。在我拨打电话的其中一个实例中,其中一个
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;
}
你的代码看起来不错。
我用以下方法测试了它:
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,
};
同样,这会产生与上面相同的结果,但它将艰苦的工作封装在一个方便的扩展方法中。
我在发布之前进行了全面测试。