Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple threading question, locking access to shared resource or entire function?

This is a paraphrasing of a question I had before. It's a simple threading question but I can't seem to understand it.

If i have shared code:

private static object objSync = new object();
private int a = 0;

    public void function()
    {
    lock(objSync)
      a += 2;

    lock(objSync)
      a += 3;
    Console.WriteLine(a.ToString());
    a=0;
    }

I can't expect 'a' to equal 5 in the end because the first thread gains a lock sets 'a' to 2 and then then the next thread can get a lock set it to '4' before the first thread is able to add 3 and you'll end up with 7 in the end.

The solution as I understand it would be to put a lock around the entire thing and then you could always expect 5. Now my question is what if between the two locks there is a a million lines of code. I can't imagine putting a lock around a million lines of code. How would you ensure thread safety but not take the fail to performance by putting a lock around a million and two lines of code?

EDIT:

This is nonsense code that I wrote for the question. The actual application is a line monitoring system. There are two screens that show the current line, one for the clerks and one for the public. The screen for the clerk accepts 'clicks' through the serial port which progresses the line by one and then updates the public screen through a notify event (see observer design pattern). The problem is they aren't synchronized if you spam the clicker. I imagine what's happening is the first screen adds to the line number, displays it and then updates the public screen, but before the public screen has a chance to show the line number from the database, the clerk clicks again and the number goes out of synch. The 'a' above represents a value I retrieve from the db and not just a simple int. There are a few places that I change the value and I need all of it to happen atomically.

like image 681
Eitan Avatar asked May 08 '26 19:05

Eitan


1 Answers

It all depends on what you define as "success". If it is a counter, that could still be the right result. If you only want to update it if it is what you thought it was, then take a snapshot inside the first lock, and likewise compare to the snapshot in the final lock. In that scenario you can replace much of the code with some Interlocked.CompareExchange. Likewise in the "counter" scenario, Interlocked.Increment is your friend.

If the code must match, then you have a few options:

  • if the compare fails, run the entire million lines again; repeat until you win the race (by matching)
  • if the compare fails, throw an exception
  • block for the million lines; it might still be a fast million lines...
  • think of some other resolution strategy that makes sense for your scenario

In the first two you should watch for polluting things by leaving any related data in an intermediate state.

And btw; the final assignment to 0 should probably also be part of the same locking strategy. Locks only work if all access respects them.

But only your scenario can tell us what the "right" behaviour in a conflict is.

like image 191
Marc Gravell Avatar answered May 11 '26 07:05

Marc Gravell



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!