C# MultiThreading - List<Object> 在另一个线程正在处理列表时锁定线程中的列表

Posted

技术标签:

【中文标题】C# MultiThreading - List<Object> 在另一个线程正在处理列表时锁定线程中的列表【英文标题】:C# MultiThreading - List<Object> Locking a list from a thread while another thread is working on the list 【发布时间】:2020-08-31 10:19:18 【问题描述】:

我的程序有 2 个主线程:

    Thread checkForNewItemsThread 将新对象添加到 volatile List&lt;Object&gt; Thread workOnListThreadList&lt;Object&gt; 上工作,并为列表中需要工作的每个项目创建另一个新的 Thread doWorkThread

我在workOnListThread 期间遇到了 ArgumentNull 异常,因为(我认为正在发生的事情):

    我的checkForNewItemsThread 正在添加到列表中,而workOnListThread 正在遍历列表。 我的doWorkThread正在从列表中删除该项目,一旦它完成了对该对象的处理。

我认为正确的做法是在workOnListThread 正在处理列表时锁定List&lt;Object&gt;,但是,我很难确定锁定的正确位置List&lt;Object&gt;,我认为这应该是正确的,但是可以使用一些指导。

volatile List<Object> List1 = new List<Object>();
private static readonly object _lock = new object();

Thread checkForNewItemsThread;
Thread workOnListThread;
Thread doWorkThread;

public void OnTimerCheckNewItems(object sender, ElapsedEventArgs args)
    
        List<Object> List1Temp = new List<Object>();
        List1Temp = getList();
        addNewItems(List1Temp);
    
private void addNewItems(List<Object> list)
    
        foreach (Object obj in list)
        
            if (!List1.Any(o => o.ID == obj.ID)) //check if object already exists
            
                List1.Add(obj);
            
        
    
private void workOnList()
    
        while (true) //loop forever
        
            while (List1.Count != 0) //while items exist
            
                lock (_lock) //lock while iterating through list
                
                    try
                    
                        for (int i = 0; i < List1.Count; i++)
                        
                            if (List1[i].Status == Status.ready && List1[i] != null)
                            
                                doWorkThread = new Thread(() => doWork(List1[i]));
                                doWorkThread.Start();
                                Thread.Sleep(3000); // wait for process to start
                            
                        
                    
                    catch (Exception e)
                    
                        Console.WriteLine(DateTime.Now.ToString(), " : ", e);
                        Log(e.ToString());
                    
                
            
        
    
private void doWork(Object obj)
    
        lock (_lock) //lock during update to status
        
            int i = List.IndexOf(obj);
            List1[i].Status = Status.pending;
        

        //work done here

        List1.Remove(alert);
    

【问题讨论】:

看看这个关于 C# 多线程的优秀资源:albahari.com/threading 【参考方案1】:

List&lt;T&gt; 类不是线程安全的,因此如果您想从多个线程同时访问它,您必须使用lock 保护对它的每个 访问。放置锁的正确位置是每个您正在读取/写入列表属性或调用其任何方法的位置。否则,您从该类的制造商那里获得的唯一保证是没有保证。正式地,该类的行为变为“未定义”,如果您试图制作一个关于其结果的正确性应该可靠的程序,这将是非常可怕的。

由于锁定会产生争用,因此您必须尽可能短地持有锁定。不要在锁内做任何与List 本身无关的事情。例如,如果您需要枚举列表中的元素并对每个元素执行某些操作,则最好获取锁,获取列表的本地快照,释放锁,最后枚举本地快照。

手动同步List 的替代方法是使用可用的concurrent collections 之一,例如ConcurrentQueueConcurrentDictionary。这些类提供了专门的 API,可以非常有效地执行原子操作,但通常使用起来很笨拙,并且不如手动同步灵活。

【讨论】:

以上是关于C# MultiThreading - List<Object> 在另一个线程正在处理列表时锁定线程中的列表的主要内容,如果未能解决你的问题,请参考以下文章

算法训练 Multithreading

6. Lab: Multithreading

Java Multithreading

C# 到 C++ 多线程,有啥问题吗?

markdown Java MultiThreading第四部分

markdown Java MultiThreading第三部分