most code examples i've seen of locking use pattern 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 montor.enter uses sort of reference pointer object lives in memory build internal dictionary of locked thread. not sure if correct, however.
i'm wondering if there ramifications of using more complex object in monitor.enter parameter. example, if multiple threads trying broadcast websocket, necessary either
- queue requests , have single thread responsible sending, or
- use locking prevent multiple threads sending same socket.
suppose websocket object used 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 uses reference pointer underlying object in memory, there theoretically no side effects fact big, complex object, instead of tiny little new object().
does have data on this?
edit: after answers below, i've come alternative pattern, extending websocket example. further feedback appreciated.
- a thin wrapper around underlying object allows creation of private readonly object use locking.
- the async method inside lock made synchronous.
note pattern doesn't take account suggestion of allowing single thread have access websocket connection (through queue system) -- i'm trying work through understanding of locking pattern 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(); } } }
my guess montor.enter uses sort of reference pointer object lives in memory build internal dictionary of locked thread. not sure if correct, however.
as others have noted, there's monitor
built-into every single .net reference type. there's not actual "dictionary" (or other collection) of held thread.
i'm wondering if there ramifications of using more complex object in monitor.enter parameter.
using reference type fine. however...
multiple threads trying broadcast websocket
in kind of situation, queueing preferred. in particular, await
cannot exist inside lock
. it's possible kind of implicit queueing using async-compatible lock, that's whole other story.
also, it's not recommended lock on argument. if example synchronous, still not recommended:
// not recommended public void sendmessage(websocket socket, arraysegment<byte> data) { lock (socket) ... }
there lock guidelines have developed on years:
- locks should private. since code can take lock, lock instance accessible other code, open possibility of deadlock. note privacy important in rule,
lock(this)
understood "not recommended", reason not because "shouldn't lockthis
", rather because "this
not private, , should lock private instances". - never call arbitrary code while holding lock. includes raising events or invoking callbacks. again, opens possibility of deadlock.
- the code within
lock
(in "critical section") should short possible. - it's best have explicit "mutex" object (i.e.,
_sync
), code readability , maintainability. - the "mutex" should documented other object(s) protecting.
- avoid code needs take multiple locks. if unavoidable, establish , document lock hierarchy locks acquired in same order.
these rules naturally result in common mutex code:
private readonly object _sync = new object();
Comments
Post a Comment