Multithreading concurrent programming of C++11

Multithreading concurrent programming of C++11 (4)

- mutexes and deadlocks

  1. mutex
    Mutex is a class object. Its function is to lock the data to be read and written. By calling lock() function, it means that only this process can share data at present, and other processes only have to wait. When this process calls unlock function, it means to unlock.
    For the last case, lock and unlock operations are performed. Lock and unlock appear in pairs. It's impossible that you occupy a lock and the execution ends, so other processes cannot obtain the lock.
#include <iostream>
#include <thread>
#include <list>
#include <mutex>

using namespace std;

class A
{
public:
	void inCommand()
	{
		for(int i = 0; i < 1000; i++)
		{
			mylock.lock();
			ticket.push_back(i);
			cout << "a command is comming" << endl;
			mylock.unlock();
		}
	}
	
	void outCommand()
	{
		for(int j = 0; j < 1000; j++)
		{
			mylock.lock();
			if(!ticket.empty())
			{
				int command = ticket.front();
				ticket.pop_front();	
				cout << "command is :" << command << endl;
				mylock.unlock();
			}
			else
			{
				cout << "there is no people" << endl;	
				mylock.unlock();
			}
		}
		
	}
	
private:
	list<int> ticket;
	mutex mylock;
};

int main(int argc, char *argv[])
{
	A a;
	thread thread1(&A::inCommand, &a);
	thread thread2(&A::outCommand, &a);
	thread1.join();
	thread2.join();

	cout << "the main thread is end;" << endl;
	return 0;
}

In this way, it can run stably.

  1. lock_guard
    In the above example, it will be found that when a problem occurs during the running of a program, and the reason for troubleshooting for half a day is that a thread is locked, but it forgot to write the unlock() function. Therefore, c + + provides a class template, lock uuguard, which is similar to the function of intelligent pointer to solve the problem of forgetting to clear the internal storage. Lock uuguard provides a constructor, as well as a destructor, and the constructor is to add Lock, destructor is unlock. When a class object is created beyond its scope, a destructor call is made.
    Therefore, it is modified:
#include <iostream>
#include <thread>
#include <list>
#include <mutex>

using namespace std;

class A
{
public:
	void inCommand()
	{
		for(int i = 0; i < 1000; i++)
		{
			lock_guard<mutex> myguard(mylock);
		//	mylock.lock();
			ticket.push_back(i);
			cout << "a command is comming" << endl;
		//	mylock.unlock();
		}
	}
	
	void outCommand()
	{
		for(int j = 0; j < 1000; j++)
		{
			lock_guard<mutex> myguard(mylock);
		//	mylock.lock();
			if(!ticket.empty())
			{
				int command = ticket.front();
				ticket.pop_front();	
				cout << "command is :" << command << endl;
			//	mylock.unlock();
			}
			else
			{
				cout << "there is no people" << endl;	
			//	mylock.unlock();
			}
		}
		
	}
	
private:
	list<int> ticket;
	mutex mylock;
};

int main(int argc, char *argv[])
{
	A a;
	thread thread1(&A::inCommand, &a);
	thread thread2(&A::outCommand, &a);
	thread1.join();
	thread2.join();

	cout << "the main thread is end;" << endl;
	return 0;
}

It can be observed that the run result is the same as the previous example, without exception.

  1. deadlock
    The condition for deadlock is that both threads must run, but both threads are waiting for the other to unlock, that is, thread 1 locks A, applies for locking B, while thread 2 locks B, applies for locking A, so thread 12 cannot continue to execute.
    You can see this in the code:
mutex mymutex1;
mutex mymutex2;

thread1:
	mymutex1.lock();
	mymutex2.lock();
	.....
	mymutex1.unlock();
	mymutex2.unlock();

thread2:
	mymutex2.lock();
	mymutex1.lock();
	....
	mymutex2.unlock();
	mymutex1.unlock();

Two threads both execute the first sentence, one is waiting for the other, waiting for each other, which is deadlock.

  1. Solution
    The function of lock() and the function of opt'u lock. The parameter of lock() is mutex, and the number of locks is the number of parameters. It means that either the locks are applied together or not together, which will not cause a deadlock state. Then, unlock() every lock when it is not needed. Another way is to add a parameter to the lock guard,
    He said that lock'guard does not execute the constructor and directly destructs the function after the end of the direct life cycle.
    You can see from the example:
#include <iostream>
#include <thread>
#include <list>
#include <mutex>

using namespace std;

class A
{
public:
	void inCommand()
	{
		for(int i = 0; i < 1000; i++)
		{
			lock(mylock1,mylock2);
			lock_guard<mutex> myguard1(mylock1,adopt_lock);
			lock_guard<mutex> myguard2(mylock2,adopt_lock);
		//	mylock.lock();
			ticket.push_back(i);
			cout << "a command is comming" << endl;
		//	mylock.unlock();
		}
	}
	
	void outCommand()
	{
		for(int j = 0; j < 1000; j++)
		{
			lock(mylock1,mylock2);
			lock_guard<mutex> myguard1(mylock1,adopt_lock);
			lock_guard<mutex> myguard2(mylock2,adopt_lock);
			//	mylock.lock();
			if(!ticket.empty())
			{
				int command = ticket.front();
				ticket.pop_front();	
				cout << "command is :" << command << endl;
			//	mylock.unlock();
			}
			else
			{
				cout << "there is no people" << endl;	
			//	mylock.unlock();
			}
		}
		
	}
	
private:
	list<int> ticket;
	mutex mylock1;
	mutex mylock2;
};

int main(int argc, char *argv[])
{
	A a;
	thread thread1(&A::inCommand, &a);
	thread thread2(&A::outCommand, &a);
	thread1.join();
	thread2.join();

	cout << "the main thread is end;" << endl;
	return 0;
}

The result is stable execution without exception.

The epidemic situation is still very serious, the community has been sealed, and we hope to finish as soon as possible and start school as soon as possible.

Published 5 original articles, won praise 0, and visited 375
Private letter follow

Tags: Programming

Posted on Tue, 11 Feb 2020 07:50:02 -0500 by fishdish