C# 设置外部类属性“string”和调用方法时如何确保线程安全

发布于 2025-01-11 10:43:44 字数 1923 浏览 0 评论 0原文

我正在致力于代码性能优化。我已经在 MethodA 中实现了 Task.WhenAll 。在 MethodA 中,我选择所有 Customer 对象(也包括客户名称和 Id),然后 MethodA 调用 MethodB它选择awaitcustomersRecordsMethodB 确实与 MethodA 存在于同一类中。

我有外部 MyLogger 类。在 MethodA 中,我需要分配 customerId

((MyLogger)log).CustomerId = customer.Id;

以便它获得每个客户的引用。使用经典循环没问题,但使用多线程时,我有时会得到不同的 customerId 值。我需要确保线程安全

MethodA

public Class MethodA{

  private static ILogger log;

public MethodA(ILogger logger){
   log = logger;
} 

 public async Task<List<Customers>> MethodA(){
    List<Customer> customers = await GetAllCustomers();
    var inboundCustomerFiles= new List<InboundCustomerFiles>(); 

    await Task.WhenAll(customers.Select(async customer =>
        {
           ((MyLogger)log).CustomerId = customer.Id; // need help here...
           log.LogDebug($"@@@ Processing Customer :: {customer.Id}: 
 and Oper8 has customerId {((MyLogger)log).CustomerId} should be same in every thread!");

            var processedCustomer = await MethodB(customer);
            inboundCustomersFiles.AddRange(processedCustomer);
        });
 }
}

MethodB

public static async Task<List<InboundCustomerFiles>> MethodB(Customer customer){
    var customerRecord = await GetCustomerRecord(customerId);

    foreach(var customer in customerRecord){
         var folderLocation = await Task.Run(() => 
         getCustomerRecordFilesLocation(customerRecordId)); 
    }
    return data;
}

MyLogger

public class MyLogger : ILogger
{
  public string CustomerId {get; set;}
  ....
}

I am working on code performance optimizing. I have implemented Task.WhenAll in MethodA. In MethodA I am picking all the Customer object (that includes customer name and Id too) Then MethodA calls MethodB which picks await customersRecords. MethodB does exist in same class as MethodA.

I have external MyLogger class. In MethodA I need to assign customerId

((MyLogger)log).CustomerId = customer.Id;

so that It get reference for each customer. With classic loop is fine but with multithread I get sometime different values of customerId. I need to ensure is thread-safe

MethodA

public Class MethodA{

  private static ILogger log;

public MethodA(ILogger logger){
   log = logger;
} 

 public async Task<List<Customers>> MethodA(){
    List<Customer> customers = await GetAllCustomers();
    var inboundCustomerFiles= new List<InboundCustomerFiles>(); 

    await Task.WhenAll(customers.Select(async customer =>
        {
           ((MyLogger)log).CustomerId = customer.Id; // need help here...
           log.LogDebug(
quot;@@@ Processing Customer :: {customer.Id}: 
 and Oper8 has customerId {((MyLogger)log).CustomerId} should be same in every thread!");

            var processedCustomer = await MethodB(customer);
            inboundCustomersFiles.AddRange(processedCustomer);
        });
 }
}

MethodB

public static async Task<List<InboundCustomerFiles>> MethodB(Customer customer){
    var customerRecord = await GetCustomerRecord(customerId);

    foreach(var customer in customerRecord){
         var folderLocation = await Task.Run(() => 
         getCustomerRecordFilesLocation(customerRecordId)); 
    }
    return data;
}

MyLogger

public class MyLogger : ILogger
{
  public string CustomerId {get; set;}
  ....
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

凉世弥音 2025-01-18 10:43:44

可以使用锁,但它会序列化所有调用。

更好的方法是避免共享可变状态。因此,要么更改 MyLogger 中的所有方法以接受客户 ID 作为参数,然后更改所有日志记录以提供此参数。或者为客户创建新的 MyLogger 对象并更改代码以使用此对象而不是共享对象。

例如,将记录器更改为

public class CustomerPrefixingLogger : ILogger
{
  private ILogger Backing {get;}
  public string CustomerId {get;}
  public CustomerPrefixingLogger(string customerId, ILogger backing){
      CustomerId = customerId;
      Backing = backing;
  }
  public void Log(int logLevel, string message) 
=> Backing.Log(logLevel, $"customer {customerId}. {message}"); 
}

现在类是不可变的。因此,如果您想创建带有客户 ID 的记录器,您需要创建一个新对象并将该对象传递给需要记录器的任何方法或类。这解决了许多问题,您不再关心日志对象是什么类型,因为您总是会创建一个新的装饰器。记录器现在是不可变的,因此您无需关心调用过程中属性的更改,并且它有助于强制实施正确的设计。

一般来说,任何类型的共享可变状态都会使任何类型的多线程变得困难。尽可能使用不可变对象和局部变量要安全得多。

You could use a lock, but it would serialize all the calls.

A better approach would be to avoid shared mutable state. So either change all the methods in MyLogger to accept the customer Id as a parameter, and change all your logging to give this parameter. Or create new MyLogger objects for customer and change your code to use this object instead of the shared object.

For example, change your logger to

public class CustomerPrefixingLogger : ILogger
{
  private ILogger Backing {get;}
  public string CustomerId {get;}
  public CustomerPrefixingLogger(string customerId, ILogger backing){
      CustomerId = customerId;
      Backing = backing;
  }
  public void Log(int logLevel, string message) 
=> Backing.Log(logLevel, 
quot;customer {customerId}. {message}"); 
}

Now the class is immutable. So if you want to create a logger with a customer Id you need to create a new object and pass this object to whatever method or class needs the logger. This solves many issues, you no longer care what type the log object is, since you will always create a new decorator. The logger is now immutable, so you do not need to care about the property changing in the middle of a call, and it helps enforce a correct design.

In general, shared mutable state of any kind will make any kind of multi threading difficult. It is far safer to use immutable objects and local variables as much as possible.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文