为 Parallel.ForEach 线程中的对象更新 属性 是否安全?
Is updating a property for an object in a Parallel.ForEach thread safe?
Public Class Customer
{
public int CustomerId{get;set;}
Public string CustomerName{get;set;}
}
public class RunParallel(List<Customer> namelessCustomers)
{
Parallel.ForEach(namelessCustomers, (customer) =>{
var customerName = CallAPIToReturnCustomerName(customer.CustomerId);
customer.CustomerName = customerName
}
}
嗨是 customer.CustomerName=customerName
线程安全的。我想要的是更新客户列表,以便每个对象都获得客户名称。
如果不是,我怎么能让这样的东西工作。您还可以解释一下为什么这不是线程安全的吗?
首先,字符串是不可变的,引用是原子的,所以属性 是线程安全的,这并不是说它不受陈旧值和数据竞争的影响——尽管这里似乎并非如此
其次,您在 parallel.ForEach
中调用了 IO 工作负载,这不是最优的。意思是,您正在阻塞和占用 线程池 线程,这些线程可以有效地卸载到 IO 完成端口。您最好让 API 调用为 async
,并使用 Task.WhenAll
例子
public async Task<Customer> CallAPIToReturnCustomerNameAsync(int customerId)
{
///await someThingAsyncHere();
}
...
async Task Process(Customer customer)
{
customer.CustomerName = await CallAPIToReturnCustomerNameAsync(customer.CustomerId);
}
var tasks = namelessCustomers.Select(Process);
await Task.WhenAll(tasks);
主要危险是并发read/writes或writes/writes。如果保证每个对象只能从单个线程访问,则默认情况下这将是线程安全的,即该列表没有同一客户的多个副本,并且在循环为 运行.
时没有其他线程可以访问客户列表
即使存在并发访问,只要类型是引用类型,或者值类型小于本机整数,这就是原子的,如本例中的情况。但是,这仅适用于单个 read/write 访问,任何涉及多个操作的操作都是不安全的。
线程安全和并发是在两种情况下值得注意的两种方法:
1- concurrent readers/writers 可以进入一个临界区。 (即使在并发编程或并行性中)并发编程是指多线程,并行性是指多核编程。
2- 作家引起读者,反之亦然。例如,在单个区域上书写之前,您必须先阅读和检查。
上面的两条规则都可能导致竞争条件,应该引起注意。
你的情况:
- 你没有任何种族,因为你只是在写作,顺序并不重要。
- 您没有再参加任何比赛,因为您在多个区域和区域(属性 不同客户)不共享。
Note: The race condition is not related to the way/stack/language of multi-X programming and you may have this situation in both multi-threading, multi-core programming. So I highly recommend you to double check your code and find critical areas and write/write or write/read moments. And you will find there is no :)
Public Class Customer
{
public int CustomerId{get;set;}
Public string CustomerName{get;set;}
}
public class RunParallel(List<Customer> namelessCustomers)
{
Parallel.ForEach(namelessCustomers, (customer) =>{
var customerName = CallAPIToReturnCustomerName(customer.CustomerId);
customer.CustomerName = customerName
}
}
嗨是 customer.CustomerName=customerName
线程安全的。我想要的是更新客户列表,以便每个对象都获得客户名称。
如果不是,我怎么能让这样的东西工作。您还可以解释一下为什么这不是线程安全的吗?
首先,字符串是不可变的,引用是原子的,所以属性 是线程安全的,这并不是说它不受陈旧值和数据竞争的影响——尽管这里似乎并非如此
其次,您在 parallel.ForEach
中调用了 IO 工作负载,这不是最优的。意思是,您正在阻塞和占用 线程池 线程,这些线程可以有效地卸载到 IO 完成端口。您最好让 API 调用为 async
,并使用 Task.WhenAll
例子
public async Task<Customer> CallAPIToReturnCustomerNameAsync(int customerId)
{
///await someThingAsyncHere();
}
...
async Task Process(Customer customer)
{
customer.CustomerName = await CallAPIToReturnCustomerNameAsync(customer.CustomerId);
}
var tasks = namelessCustomers.Select(Process);
await Task.WhenAll(tasks);
主要危险是并发read/writes或writes/writes。如果保证每个对象只能从单个线程访问,则默认情况下这将是线程安全的,即该列表没有同一客户的多个副本,并且在循环为 运行.
时没有其他线程可以访问客户列表即使存在并发访问,只要类型是引用类型,或者值类型小于本机整数,这就是原子的,如本例中的情况。但是,这仅适用于单个 read/write 访问,任何涉及多个操作的操作都是不安全的。
线程安全和并发是在两种情况下值得注意的两种方法: 1- concurrent readers/writers 可以进入一个临界区。 (即使在并发编程或并行性中)并发编程是指多线程,并行性是指多核编程。 2- 作家引起读者,反之亦然。例如,在单个区域上书写之前,您必须先阅读和检查。 上面的两条规则都可能导致竞争条件,应该引起注意。 你的情况:
- 你没有任何种族,因为你只是在写作,顺序并不重要。
- 您没有再参加任何比赛,因为您在多个区域和区域(属性 不同客户)不共享。
Note: The race condition is not related to the way/stack/language of multi-X programming and you may have this situation in both multi-threading, multi-core programming. So I highly recommend you to double check your code and find critical areas and write/write or write/read moments. And you will find there is no :)