我看到锁定的大多数代码示例都使用这样的模式:
private static int _counter = 0;
private static readonly object _sync = new object();
public void DoWork()
{
int counter;
lock (_sync)
{
counter = _counter++;
}
// etc ...
}
我的猜测是,Montor.Enter使用某种类型的引用指向生活在内存中的对象,以构建一个由什么线程锁定的内部字典。但是,不确定这是否正确。
我想知道在Monitor.Enter参数中是否存在使用更复杂对象的任何后果。例如,如果多个线程试图广播到WebSocket,则需要
假设WebSocket对象本身用于锁定:
public async Task SendMessage(WebSocket socket, ArraySegment<byte> data)
{
lock (socket)
{
if (socket.State == WebSocketState.Open)
{
await socket.SendAsync(
data,
WebSocketMessageType.Text,
true,
CancellationToken.None);
}
}
}
如果Monitor.Enter只是使用一个指向内存中底层对象的引用指针,理论上它不会产生副作用,因为它是一个大而复杂的对象,而不是一个小小的新对象()。
有没有人有这方面的数据?
编辑:在下面的一些答案之后,我提出了另一种模式,扩展了WebSocket示例。任何进一步的反馈将不胜感激。
请注意,此模式并未考虑仅允许单个线程访问WebSocket连接(通过队列系统)的建议 - 我主要尝试通过我的理解具有特定示例的锁定模式。
public class SocketWrapper
{
private readonly object _sync = new object();
public WebSocket Socket { get; private set; }
public SocketWrapper(WebSocket socket)
{
this.Socket = socket;
}
public async Task SendMessage(ArraySegment<byte> data)
{
await Task.Yield();
lock (this._sync)
{
var t = await this.Socket.SendAsync(
data,
WebSocketMessageType.Text,
true,
CancellationToken.None);
t.Wait();
}
}
}
答案 0 :(得分:3)
锁机制使用对象的标头来锁定,对象的复杂程度并不重要,因为标头是机制所使用的。但是锁定的经验法则很好。
object
,因为有人可能会锁定自己see this answer以获取更多信息您可以在MSDN上阅读有关lock关键字和Monitor.Enter的更多信息:
答案 1 :(得分:2)
这很好。 .NET使用一些Object头来有效地创建和使用spinlock,如果失败,它会使用一个信号量池。
在任何一种情况下,它都基于.NET中所有对象所具有的底层Object头。包含对象的复杂程度和简单程度无关紧要。
答案 2 :(得分:1)
我的猜测是,Montor.Enter使用某种类型的引用指向生活在内存中的对象,以构建一个由什么线程锁定的内部字典。但是,不确定这是否正确。
正如其他人所说,实际上每个.NET引用类型都内置了Monitor
。任何线程都没有实际的“字典”(或任何其他集合)。
我想知道在Monitor.Enter参数中是否存在使用更复杂对象的任何后果。
使用任何引用类型都可以。然而...
多个线程试图广播到WebSocket
在这种情况下,排队是首选。特别是await
中不能存在lock
。通过使用异步兼容锁可以进行一种隐式排队,但这是另一个故事。
此外,不建议锁定参数。如果此示例是同步的,则仍然不建议:
// NOT recommended
public void SendMessage(WebSocket socket, ArraySegment<byte> data)
{
lock (socket)
...
}
多年来已经制定了一些锁定指南:
lock(this)
通常被理解为“不推荐”,但原因不是因为您“不应该锁定{{ 1}}“,而是因为”this
不是私有的,你应该只锁定私有实例“。this
(在“关键部分”)中的代码应尽可能短。lock
)。这些规则自然导致常见的互斥代码:
_sync