我目前正在开发一个多线程项目。主要用于学习目的。
我在这个项目中的作用是编写一个服务器应用程序
但我得到的一点是,整个变量锁定有点令人困惑,因为我不确切知道它是如何工作的以及何时/我必须使用它。
假设我的class ClientHandler
包含List<Client> clientList
ClientHandler
有一个返回ClientList
的属性clientList
:
private List<Client> clientList;
public List<Client> ClientList
{
get { lock (lockObject) return clientList; }
set { lock (lockObject) clientList = value; }
}
NetworkHandler
运行第二个Thread
,与List<>
一起使用
在此网络Thread
中,检索到的List<>
在使用时被另一个object
锁定
我的问题是这种锁定是如何起作用的
如果我lock
网络List<>
中的Thread
(object
与ClientHandler
不同)是线程安全的吗?因此,一般情况下,如果lock
变量为object
的变量,是否会锁定其他所有尝试访问它的变量?
我只是想确保List<>
在另一个Thread
处理时无法更改。
答案 0 :(得分:3)
我假设你希望List本身通过Add / Remove / Clear / foreach方法在不同的线程上传递和操作,但不对象本身。
如果是这种情况,那么List本身需要在每个操作(添加,删除等)上实现内部锁定机制。列表会创建lockObject
,而不是您。
显然List<T>
无法执行此操作,因此您需要从中派生或实现自己的IList / ICollection类,或者您只需使用System.Collections.Concurrent
命名空间中的集合即可专为此目的而设计,例如ConcurrentDictionary
。
如果你想让多个线程可以访问集合中的对象,那么你必须让这些对象线程安全......这是一个完全独立的步骤。
有关lock关键字如何工作的简要说明:
当您通过指定lock (someObject) { }
来锁定对象时,代码块中的所有内容都将仅在同一对象上的每个其他锁定实例未执行时执行。
在锁定代码块的开头,它在某处设置了一个“我正在保留此对象”的线程安全标志,并在锁定块的末尾显示“我不再保留此对象”。在锁定块开始时,如果它试图锁定一个对象但该对象已经被锁定,那么它将无限期地等待,直到它成功获得锁定,阻止该进程中的调用线程。
在您的示例中,您使用的是内联代码块:
get { lock (lockObject) return clientList; }
相当于:
get
{
lock (lockObject)
{ // Lock starts here
return clientList;
} // Lock ends here
}
因此,如果您访问该属性,则只要将对象提供给属性的调用者,该对象就会被解锁。然后调用者可以继续调用该List上的Add()方法,在内部它将访问没有锁定机制的内部集合,因此它不是线程安全的。
相反,List每次访问内部字段时都需要调用锁,例如_innerList
字段,如下所示:
public void Add(object item)
{
lock (_innerList)
{
_innerList.Add(item);
}
}