4

Another possibly inane style question:

How should concurrency be locked? Should the executor or caller be responsible for locking the thread?

e.g. in no particular language...

Caller::callAnotherThread() {
    _executor.method();
}

Executor::method() {
    _lock();
    doSomething();
    _unlock();
}

OR

Caller::callAnotherThread() {
    _executor.lock()
    _executor.method();
    _executor.unlock()
}

Executor::method() {
    doSomething();
}

I know little about threading and locking, so I want to make sure the code is robust. The second method allows thread unsafe calls... you could technically call _executor.method() without performing any kind of lock.

Help?

Thanks,

Stephen Furlani
  • 6,794
  • 4
  • 31
  • 60
  • Locking/unlocking (critical section handling) is very dependend on the programming language. Some have already built-in support for critical sections, some don't. Do you have a special language in mind? (pthreads suggests C/C++ to me) – Kosi2801 Sep 10 '10 at 19:38

3 Answers3

5

The callee, not the caller should do the locking. The callee is the only one who knows what needs to be synchronized and the only one who can ensure that it is. If you leave locking up to the callers, you do three bad things:

  1. You increase the burden on users of your function/class, increasing design viscosity.
  2. You make it possible for callers to update shared state without taking the lock.
  3. You introduce the possibility of deadlocks if different functions take multiple locks in different order.
Peter Ruderman
  • 12,241
  • 1
  • 36
  • 58
  • +1. Good points, but I think there are valid situations where you could lock outside as well. – Skurmedel Sep 10 '10 at 19:45
  • Good, that's how I have it so far. :D – Stephen Furlani Sep 13 '10 at 15:50
  • I respectfully disagree with this dogma. Here is a good discussion of internal vs. external locking in the Boost.Thread documentation: [link](https://www.boost.org/doc/libs/1_77_0/doc/html/thread/synchronization.html#thread.synchronization.tutorial) I think it makes a good case for the advantages of external locking. See especially the section beginning: "Internal locking is insufficient for many real-world synchronization tasks..." – jwd Oct 23 '21 at 01:26
  • I haven't come across a situation that _required_ external locking, but I grant that such situations might exist. If it really is required then, obviously, that's what you must do. Nevertheless, I stand by my advice. External locking is dangerous (for the reasons given above), and should be avoided. Don't do it unless you have no other choice. – Peter Ruderman Oct 25 '21 at 21:14
  • I haven't come across a situation that _required_ external locking, but I grant that such situations might exist. If it really is required then, obviously, that's what you must do. Nevertheless, I stand by my advice. External locking is dangerous (for the reasons given above), and should be avoided. Don't do it unless you have no other choice. – Peter Ruderman Oct 25 '21 at 21:14
1

We are learning that external locking offers advantages if you need to do several interrelated granular operations at once, or work with a reference to an internal structure - you can hold a lock as long as you need your set of work to be safe from other threads.

An example: A container that manages a list of items might want to provide an api to get a mutable reference to one item. Without external locking, as soon as the function call finishes, another thread could potentially lock and mutate data. A plausible solution is to return a copy of the one item, but this is inefficient.

That being said, for some cases, internal locking can have a cleaner api, provided you can be sure that you won't want to preserve a lock longer than one function call.

jv-dev
  • 646
  • 7
  • 14
1

If you use locks internally, you have to note it on manual documentation. Or your code will bottleneck of parallel execution, and users will be hard to know the truth.

eonil
  • 83,476
  • 81
  • 317
  • 516