Most code examples I've seen of locking use a pattern like this:
private static int _counter = 0;
private static readonly object _sync = new object();
public void DoWork()
{
int counter;
lock (_sync)
{
counter = _counter++;
}
// etc ...
}
My guess is that Montor.Enter uses some sort of reference pointer to the object that lives in memory to build some internal dictionary of what is locked by what thread. Not sure if this is correct, however.
I'm wondering if there are any ramifications of using a more complex object in the Monitor.Enter parameter. For example, if multiple threads were trying to broadcast to a WebSocket, it would be necessary to either
- Queue up the requests and have a single thread be responsible for sending, or
- Use locking to prevent multiple threads from sending to the same socket.
Suppose the WebSocket object itself was used for the lock:
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);
}
}
}
If Monitor.Enter simply uses a reference pointer to the underlying object in memory, there would theoretically be no side effects to the fact that it is a big, complex object, instead of a tiny little new object().
Does anyone have any data on this?
Edit: After some answers below, I've come up with an alternative pattern, extending the WebSocket example. Any further feedback would be appreciated.
- A thin wrapper around the underlying object allows for the creation of a private readonly object to use for locking.
- The async method inside the lock is made synchronous.
Note that this pattern doesn't take into account the suggestion of only allowing a single thread to have access to the WebSocket connection (through a queue system) -- I'm mostly trying to work through my understanding of a locking pattern with a specific example.
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();
}
}
}