1

Consider the following code snippet

int index = 0;
av::utils::Lock lock(av::utils::Lock::EStrategy::eMutex); // Uses a mutex or a spin lock based on specified strategy.

void fun()
{
    for (int i = 0; i < 100; ++i)
    {
        lock.aquire();
        ++index;
        std::cout << "thread "  << std::this_thread::get_id() << " index = " << index << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        lock.release();
    }
}

int main()
{
    std::thread t1(fun);
    std::thread t2(fun);

    t1.join();
    t2.join();
}

The output that I get with a mutex used for synchronization is first thread 1 gets executed completely followed by thread 2. While using a spinlock(implemented using std::atomic_flag), I get the order of execution between the threads which is interleaved (one iteration of thread 1 followed by another iteration of thread 2). The latter case happens irrespective of the delay I add in execution of the iteration.

I understand that a mutex only guarantees mutual exclusion and not the order of execution. The question I have is if I want to have an execution order such that two threads are executed in an interleaved manner, is using spinlocks a recommended strategy or not?

Arun
  • 3,138
  • 4
  • 30
  • 41
  • If you want to force interleaved execution, you need to have each thread wait its turn instead of racing for the spinlock. – Raymond Chen Feb 26 '20 at 05:23
  • Ok. So it is possible that with a spin lock if there are multiple threads involved, I could still have a thread winning the race and getting executed always... how can I avoid this... I can think of using a condition variable so that after executing a thread notifies the other and then the other proceeds and so on... is that a good approach or something better is possible...i.e. something which is directly available in the library instead of hand crafting the solution – Arun Feb 26 '20 at 05:28
  • Please be more specific when you say "directly available in **the** library" - which library are you referring to? Your code above uses both the C++ standard library and some other mysterious library called "av". – Raymond Chen Feb 26 '20 at 05:35
  • Oh...Sorry about that. I meant the standard library. – Arun Feb 26 '20 at 05:39
  • 1
    You can have two mutexes that take turns. Or you can have a condition variable that guards another variable that says whose turn it is. – Raymond Chen Feb 26 '20 at 05:41
  • I understood the latter solution... but not sure how to implement the former... let me think a bit... – Arun Feb 26 '20 at 05:46
  • 2
    I don't see the purpose of this. Just why? – ALX23z Feb 26 '20 at 05:56
  • I can't think of a specific use case on top of my head... but any case where in you would want two activities to be executed one after the other in a sequential fashion, but on different threads. – Arun Feb 26 '20 at 06:00
  • You probably want [a ticket spinlock](https://mfukar.github.io/2017/09/08/ticketspinlock.html) ([wikipedia page](https://en.wikipedia.org/wiki/Ticket_lock)). – dewaffled Feb 26 '20 at 06:03
  • 2
    Please consider not asking a yes/no question. The answer is clearly "no" as nothing about the c++ specification requires such behavior and you can't make any assumptions about how the underlying hardware and operating system will run now and forever. – xaxxon Feb 26 '20 at 06:14
  • 2
    I don't quite get the motivation to run threads mutual exclusive. When I introduce threads I want to run them concurrently - with as less as possible synchronization aka communication. Of course, often it cannot be done completely without sync. e.g. for producer-consumer or in case of shared resources. But, to run threads interleaved by intention... In this case, I would consider whether this couldn't be done better without all the additional trouble of threads. (Sorry, just my two cents...) – Scheff's Cat Feb 26 '20 at 06:17
  • A good goal--can't always achieve it, but it's worth-while to try--is to design your threads in such a way that, whenever some task needs to be performed, it doesn't matter which thread performs the task – Solomon Slow Feb 26 '20 at 13:19

1 Answers1

1

The output that I get with a mutex ... is first thread 1 [runs through the whole loop] followed by thread 2.

That's because of how your loop uses the lock: The very last thing the loop body does is, it unlocks the lock. The very next thing it does at the start of the next iteration is, it locks the lock again.

The other thread can be blocked, effectively sleeping, waiting for the mutex. When your thread 1 releases the lock, the OS scheduler may still be running its algorithms, trying to figure out how to respond to that, when thread 1 comes 'round and locks the lock again.

It's like a race to lock the mutex, and thread 1 is on the starting line when the gun goes off, while thread 2 is sitting on the bench, tying its shoes.

While using a spinlock...the order of execution between the threads which is interleaved

That's because the "blocked" thread isn't really blocked. It's still actively running on a different processor while it waits. It has a much better chance at winning the lock when the first thread releases it.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57