c# - Atomic wait-then-take operation on number -
i have following class:
public class atomiclong { private long initial; private long value; public atomiclong(long value = 0) { this.initial = value; this.value = value; } public class handle : idisposable { private atomiclong source; private long amount; public handle(atomiclong source, long amount) { this.source = source; this.amount = amount; } public void dispose() { if (source == null) return; interlocked.add(ref source.value, amount); source = null; } } public handle claim(long amount) { if (amount > initial) throw new argumentoutofrangeexception("amount", amount, "must no more initial amount."); if (amount < 0) throw new argumentoutofrangeexception("amount", amount, "must nonnegative."); while (true) { var oldvalue = interlocked.read(ref value); var newvalue = oldvalue - amount; if (newvalue >= 0 && oldvalue == interlocked.compareexchange(ref value, newvalue, oldvalue)) { return new handle(this, amount); } } } }
an example usage of can have single atomiclong unusedmemory
represents current number of bytes of memory available set of workers. (it's not meant anywhere near exact - it's rough measure.) on bunch of different worker threads:
while (true) { var unitofwork = waitforunitofwork(); long requiredmemory = unitofwork.requiredmemory; using (var handle = unusedmemory.claim(requiredmemory)) { //wait until requirememory can claimed unusedmemory //do work reserved memory, represented handle //when handle disposes, memory released unusedmemory } }
the problem atomiclong
class calls claim
busy-wait until return. i'd fix using kind of os-level wait handle abstraction.
can suggest how go doing that?
motivation
consider following scenario:
- unusedmemory starts initial value of 10gb (
10 << 30
) - 100 worker threads
- 10 units of work, each taking 10gb , 1 minute perform
- first worker calls
claim(10 << 30)
, nearly-immediately returns- it begins doing work finish after 1 minute
- some 9 other workers make identical call
claim(10 << 30)
, "bad" busy wait 1 minute- 9 threads doing
while(true){/*do nothing*/}
loop inclaim
method! - lots of needless cpu usage
- 9 threads doing
- the rest of workers (90) "good" os-level wait in
waitforunitofwork()
method
the important point: claim
"cheap" if requested amount
of memory available claimed. if isn't, busy-waiting happens until available.
just entirely clear, in claim
method, i'm pointing out exact expression makes difference (newvalue >= 0
):
while (true) { var oldvalue = interlocked.read(ref value); var newvalue = oldvalue - amount; if (newvalue >= 0 && // <--------------------------- problem oldvalue == interlocked.compareexchange(ref value, newvalue, oldvalue)) { return new handle(this, amount); } }
the question not whether interlocked.compareexchange
going expensive - i'm aware it's cheap. question how deal busy-waiting occurs in case when amount
caller wants claim
greater amount
in atomiclong
.
if have different approach solving kind of problem, or see flaw in have, i'd hear too!
you have several options.
for instance, can create smarter busy-wait, putting active thread sleep given time interval, doesn't check condition, periodically.
another solution create custom event , wait event in active thread, , can define custom event fulfill task believe.
you can read more events here. can read custom event creation here.
Comments
Post a Comment