首页 > 解决方案 > 在单独的线程中同步更新属性

问题描述

我有一个继承自BackgroundService. 它在应用程序的整个生命周期内运行一个任务,在该任务中,我有一个循环进行一些计算,其中一些使用可设置的属性,这些属性可以从另一个线程设置。对于循环的一次迭代,我必须确保这些属性都不会改变,所以我的所有计算都使用相同的值。

我最初处理这个问题的方法是让我的 require 属性实际上只是将一个动作放在队列中,然后在我的循环开始时执行这些动作。所以,我最终得到了这样的结果:

private MyConfig Config { get; set; }
private readonly ConcurrentQueue<Action> queue = new ConcurrentQueue<Action>();

public void UpdateConfig(MyConfig config)
{
    queue.Enqueue(() => Config = config);
}

protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{
    await Task.Run(async () =>
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            for (int i = queue.Count; i > 0; i--)
            {
                queue.TryDequeue(out Action element);
                element?.Invoke();
            }
        }
        //now do some stuff with the updated config and maybe other properties too
    });             
}

我觉得可能有更好的方法。我应该锁定这些属性吗?在这一点上,我不确定最好的解决方案。

标签: c#multithreadingthread-safetylocking

解决方案


以下是我认为的标准模式:使用lock对象同步对共享字段的访问。

private readonly object _locker = new object();
private MyConfig _config;

public void UpdateConfig(MyConfig config)
{
    lock (_locker) _config = config;
}

protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{
    await Task.Run(async () =>
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            MyConfig localConfig; lock (_locker) localConfig = _config;
            // Do stuff with the localConfig. Don't use the shared _config field.
        }
    });             
}

MyConfig如果类型是不可变的,这应该是线程安全的。它应该同样适用于classor 或struct MyConfig


推荐阅读