C# – 正确锁定列表在多线程场景中

18 篇文章 0 订阅

好的,我只是无法正确理解多线程场景。很抱歉再次提出类似的问题,我只是在互联网上看到了许多不同的“事实”。

public static class MyClass {
    private static List<string> _myList = new List<string>;
    private static bool _record;

    public static void StartRecording()
    {
        _myList.Clear();
        _record = true;
    }

    public static IEnumerable<string> StopRecording()
    {
        _record = false;
        // Return a Read-Only copy of the list data
        var result = new List<string>(_myList).AsReadOnly();
        _myList.Clear();
        return result;
    }

    public static void DoSomething()
    {
        if(_record) _myList.Add("Test");
        // More, but unrelated actions
    }
}

这个想法是,如果 Recording 被激活,对 DoSomething() 的调用会被记录在一个内部 List 中,并在 StopRecording() 被调用时返回。

我的规格是这样的:

  • StartRecording 不被认为是线程安全的。用户应该在没有其他线程调用 DoSomething() 时调用它。但如果能以某种方式实现,那就太好了。
  • StopRecording 也不是正式的线程安全的。同样,如果可以的话,那就太好了,但这不是必需的。
  • DoSomething 必须是线程安全的

通常的方法似乎是:

    public static void DoSomething()
    {
        object _lock = new object();
        lock(_lock){
            if(_record) _myList.Add("Test");
        }
        // More, but unrelated actions
    }

或者,声明一个静态变量:

    private static object _lock;

    public static void DoSomething()
    {
        lock(_lock){
            if(_record) _myList.Add("Test");
        }
        // More, but unrelated actions
    }

但是,这个答案说这不会阻止其他代码访问它。

所以我想知道

  • 我将如何正确锁定列表?
  • 我应该在我的函数中创建锁对象还是作为静态类变量?
  • 我也可以将 Start 和 StopRecording 的功能包装在一个锁块中吗?
  • StopRecording() 做了两件事:将布尔变量设置为 false(以防止 DoSomething() 添加更多内容),然后复制列表以将数据副本返回给调用者)。我假设 _record = false; 是原子的并且会立即生效吗?所以通常我根本不用担心这里的多线程,除非其他一些线程再次调用 StartRecording()?

在一天结束时,我正在寻找一种方式来表达“好的,这个列表现在是我的,所有其他线程都必须等到我完成它”。

最佳解决方案

我将在这里锁定 _myList 本身,因为它是私有的,但使用单独的变量更常见。改进几点:

public static class MyClass 
{
    private static List<string> _myList = new List<string>;

    private static bool _record; 


    public static void StartRecording()
    {
        lock(_myList)   // lock on the list
        {
           _myList.Clear();
           _record = true;
        }
    }

    public static IEnumerable<string> StopRecording()

    {
        lock(_myList)
        {
          _record = false;
          // Return a Read-Only copy of the list data
          var result = new List<string>(_myList).AsReadOnly();
          _myList.Clear();
          return result;
        }
    }

    public static void DoSomething()

    {
        lock(_myList)
        {
          if(_record) _myList.Add("Test");
        }
        // More, but unrelated actions
    }
}

请注意,此代码用于lock(_myList)同步对 _myList_record 的访问。您需要同步这两个上的所有操作。

并且同意这里的其他答案,lock(_myList)对 _myList 没有任何作用,它只是使用 _myList 作为令牌(大概作为 HashSet 中的键)。所有方法都必须通过使用相同令牌请求许可来公平竞争。另一个线程上的方法仍然可以使用 _myList 而无需先锁定,但会产生不可预知的结果。

我们可以使用任何令牌,所以我们经常专门创建一个:

private static object _listLock = new object(); 

然后使用lock(_listLock)而不是lock(_myList)到处使用。

如果 myList 是公开的,那么这种技术是可取的,如果您重新创建了 myList 而不是调用 Clear(),这将是绝对必要的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值