正在更新并行对象的属性.是否每个线程都是安全的?


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
}
}

Hi是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);

主要的危险是并发读/写或写/写。如果保证每个对象只能由单个线程访问,那么默认情况下这将是线程安全的,即列表中没有相同客户的多个副本,并且在循环运行时没有其他线程可以访问客户列表。

即使存在并发访问,只要类型是引用类型,或者值类型小于本机整数,就像本例中的情况一样,这也是原子性的。然而,这只适用于单个读/写访问,任何涉及多个操作的操作都是不安全的。

线程安全和并发性是两种值得注意的方法:1-并发读/写可以进入一个关键区域。(即使在并发编程或并行编程中也是如此)通过并发编程,我指的是多线程,通过并行编程,我指的是多核编程。作者导致读者,反之亦然。例如,在写一个区域之前,你必须阅读和检查。上述两个规则都可能导致竞争条件,应该引起注意。在你的例子中:

  • 你没有任何竞争,因为你只是在写,顺序不重要。
  • 你没有任何种族,因为你写在几个区域和区域(不同客户的财产)不共享。

注意:竞争条件与多x编程的方式/堆栈/语言无关,您可能在多线程,多核编程中遇到这种情况。所以我强烈建议你仔细检查你的代码,找出关键的地方和写/写或写/读的时刻。你会发现没有:)

最新更新