C# foreach 内的线程安全闭包

标签 c# .net multithreading thread-safety

我编写了一个代码块,但我不确定它是否是线程安全的。

List<Task> tasks = new List<Task>();
foreach (KeyValuePair<string, string> kvp in result)
{
    var t = new Task(async () => 
    {
        int retries = 0;
        bool success = false;
        try
        {
            while (retries <= _maxRetries && !success)
            {
                await doSomething(kvp.Value);
                success = true;
            }
        }
        catch (Exception e)
        {
            retries++;
        }

        if (retries == _maxRetries)
        {
            //TODO: need to do smth about it
        }
    });
    tasks.Add(t);
    t.Start();
}
await Task.WhenAll(tasks);

我可以相信这样一个事实吗:当编译器设置任务时,他使用一个安全的 value,意思是只要我在循环中并且任务还没有声明,设置的值就可以了?

因为,我认为在第一次重试 while 循环之后,kvp对象不会像任务第一次运行时那样。

如果它实际上不是线程安全的(我认为它确实不是),那么如何修复它?

最佳答案

如果您使用的是 C# 5,那么您的代码就没有问题; foreach 的语义已更改,以便循环变量在逻辑上作用于每次迭代。每Eric Lippert :

In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time.

如果您使用的是 C# 4 或更早版本,那么您应该将 kvp 复制到闭包的局部变量。

您的代码有一个不相关的错误:通过 Task 构造函数初始化的任务不会等待作为参数传递给它的异步函数委托(delegate)的完成。相反,您应该对此类委托(delegate)使用 Task.Run

有一种更简单(安全)的方法可以实现此目的:

var tasks = result.Select(kvp => Task.Run(async () =>
{
    int retries = 0;
    bool success = false;
    // ...
})).ToList();

await Task.WhenAll(tasks);

关于C# foreach 内的线程安全闭包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36595378/

相关文章:

c# - MVC 5 - 角色 - IsUserInRole 和将用户添加到角色

c# - Entity Framework Sum() 性能

java - 从基于 Swing 的应用程序中的线程(ETC 除外)接收更新

c# - 根据比较结果自动交换值(value)

python - 带有 Tkinter GUI 的 Twisted TCP 服务器

c# - 统一.WebApi |确保 Controller 有一个无参数的公共(public)构造函数

c# - ASP.NET MVC 6 中的 MVC Controller 和 Web API Controller 有什么区别?

c# - 在 Azure Eventhub 中发布数据时出现未经授权的错误

c# - 我想要一种用于元编程的预处理语言

C# :how to have message box within an event handler that freezes the application until Ok is pressed?