<1>, C++ implements multi-thread synchronization processing: controlling the output sequence of ABC, outputting 10 groups, mutex+condition_variable

Table of Contents

need:

analyze:

Some code implementation:

1. Operations implemented using only flag bits:

2. For greater security, the mutex lock code is added:

3. Use it with unique_lock to make the code safer

Four: Use guard lock lock_guard to work with it

5. You can also use condition variables to process, which will become more efficient.

6. Output result display:


Requirement:

There are three threads that can output characters A, B, and C respectively. Now we are trying to control the output order so that the order is ABCABC…ABC [10 groups in total], and each thread can only be started once, not multiple times. start up.

Analysis:

To use multi-threading in standard C++, you can use the class thread, 3 threads. We can be more straightforward and directly implement 3 thread functions, loop in the function to output the corresponding characters, and control each loop 10 times. ;

At the same time, if you want to control the output sequence, the first way that comes to mind is to directly add a flag to control, which is also excellent and can basically achieve the output effect of ABC;

Although the method of processing through a flag bit can achieve the effect, it is not safe enough. It is very likely that multiple threads will operate a global variable at the same time, causing resource contention problems. In order to ensure safety, corresponding locks can be added on this basis. To deal with synchronization issues, such as adding a mutex lock, you can effectively prevent resource contention issues.

Now that mutex locks have been used, can we optimize the following on this basis and cooperate with the guard lock lock_guard or the unique lock unique_lock to solve the possible abnormal deadlock problem?

Some code implementations:

1. Operations implemented only using flag bits:

#include <iostream>
#include <thread>
using namespace std;

int g_flag = 0; // Global flag bit 0 outputs A, 1 outputs B, 2 outputs C
void outA()
{
for (int i = 0; i < 10;) {
if (g_flag == 0) { // Flag bit is 0, output A
cout << "A"; // Output the target character A
+ + i; // Increment the number of times
g_flag = 1; // Modify the flag bit to 1 and let B output
}
// If the condition is not met, it will continue to loop endlessly
}
}
void outB()
{
for (int i = 0; i < 10;) {
if (g_flag == 1) { // Flag bit is 1, output B
cout << "B";
+ + i;
g_flag = 2; // Modify the flag bit to 2 and let C output
}
}
}
void outC()
{
for (int i = 0; i < 10;) {
if (g_flag == 2) { // Flag bit is 2, output C
cout << "C";
+ + i;
g_flag = 0; // Modify the flag bit to 0 and let A output
}
}
}

int main()
{
thread tA( & amp;outA); // A's thread
tA.detach();
thread tB( & amp;outB); // B's thread
tB.detach();
thread tC( & amp;outC); // C's thread
tC.join(); // The last thread needs to be blocked with join to prevent the main function from ending immediately
    return 0;
}

2. For greater security, a mutex lock code is added:

#include <iostream>
#include <mutex>
#include <thread>
using namespace std;

int g_flag = 0; // Global flag bit 0 outputs A, 1 outputs B, 2 outputs C
mutex g_mutex;

void outA()
{
for (int i = 0; i < 10;) {
if (g_flag == 0) { // Flag bit is 0, output A
cout << "A"; // Output the target character A
+ + i; // Increment the number of times
g_mutex.lock();
g_flag = 1; // Modify the flag bit to 1 and let B output
g_mutex.unlock();
}
// If the condition is not met, it will continue to loop endlessly
}
}
void outB()
{
for (int i = 0; i < 10;) {
if (g_flag == 1) { // Flag bit is 1, output B
cout << "B";
+ + i;
g_mutex.lock();
g_flag = 2; // Modify the flag bit to 2 and let C output
g_mutex.unlock();
}
}
}
void outC()
{
for (int i = 0; i < 10;) {
if (g_flag == 2) { // Flag bit is 2, output C
cout << "C";
+ + i;
g_mutex.lock();
g_flag = 0; // Modify the flag bit to 0 and let A output
g_mutex.unlock();
}
}
}

int main()
{
thread tA( & amp;outA); // A's thread
tA.detach();
thread tB( & amp;outB); // B's thread
tB.detach();
thread tC( & amp;outC); // C's thread
tC.join(); // The last thread needs to be blocked with join to prevent the main function from ending immediately
    return 0;
}

3. Use it with unique_lock, the code is more secure

#include <iostream>
#include <mutex>
#include <thread>
using namespace std;

int g_flag = 0; // Global flag bit 0 outputs A, 1 outputs B, 2 outputs C
mutex g_mutex;

void outA()
{
for (int i = 0; i < 10;) {
if (g_flag == 0) { // Flag bit is 0, output A
cout << "A"; // Output the target character A
+ + i; // Increment the number of times
//g_mutex.lock();
unique_lock<mutex> ul(g_mutex); // The unique lock is automatically locked when instantiated and automatically unlocked when leaving the scope.
g_flag = 1; // Modify the flag bit to 1 and let B output
//g_mutex.unlock();
}
// If the condition is not met, it will continue to loop endlessly
}
}
void outB()
{
for (int i = 0; i < 10;) {
if (g_flag == 1) { // Flag bit is 1, output B
cout << "B";
+ + i;
//g_mutex.lock();
unique_lock<mutex> ul(g_mutex);
g_flag = 2; // Modify the flag bit to 2 and let C output
//g_mutex.unlock();
}
}
}
void outC()
{
for (int i = 0; i < 10;) {
if (g_flag == 2) { // Flag bit is 2, output C
cout << "C";
+ + i;
//g_mutex.lock();
unique_lock<mutex> ul(g_mutex);
g_flag = 0; // Modify the flag bit to 0 and let A output
//g_mutex.unlock();
}
}
}

int main()
{
thread tA( & amp;outA); // A's thread
tA.detach();
thread tB( & amp;outB); // B's thread
tB.detach();
thread tC( & amp;outC); // C's thread
tC.join(); // The last thread needs to be blocked with join to prevent the main function from ending immediately
    return 0;
}

Four: Use guard lock lock_guard to use together

#include <iostream>
#include <mutex>
#include <thread>
using namespace std;

int g_flag = 0; // Global flag bit 0 outputs A, 1 outputs B, 2 outputs C
mutex g_mutex;

void outA()
{
for (int i = 0; i < 10;) {
if (g_flag == 0) { // Flag bit is 0, output A
cout << "A"; // Output the target character A
+ + i; // Increment the number of times
//g_mutex.lock();
//unique_lock<mutex> ul(g_mutex); // Unique lock, automatically locked when instantiated, automatically unlocked when leaving the scope destructor
lock_guard<mutex> lg(g_mutex); // Guard lock can also play the role of automatic locking and automatic unlocking
g_flag = 1; // Modify the flag bit to 1 and let B output
//g_mutex.unlock();
}
// If the condition is not met, it will continue to loop endlessly
}
}
void outB()
{
for (int i = 0; i < 10;) {
if (g_flag == 1) { // Flag bit is 1, output B
cout << "B";
+ + i;
//g_mutex.lock();
//unique_lock<mutex> ul(g_mutex);
lock_guard<mutex> lg(g_mutex); // Guard lock can also play the role of automatic locking and automatic unlocking
g_flag = 2; // Modify the flag bit to 2 and let C output
//g_mutex.unlock();
}
}
}
void outC()
{
for (int i = 0; i < 10;) {
if (g_flag == 2) { // Flag bit is 2, output C
cout << "C";
+ + i;
//g_mutex.lock();
//unique_lock<mutex> ul(g_mutex);
lock_guard<mutex> lg(g_mutex); // Guard lock can also play the role of automatic locking and automatic unlocking
g_flag = 0; // Modify the flag bit to 0 and let A output
//g_mutex.unlock();
}
}
}

int main()
{
thread tA( & amp;outA); // A's thread
tA.detach();
thread tB( & amp;outB); // B's thread
tB.detach();
thread tC( & amp;outC); // C's thread
tC.join(); // The last thread needs to be blocked with join to prevent the main function from ending immediately
    return 0;
}

5. You can also use condition variables to process, which will become more efficient

Due to the length, please go to the next document to view the combined use of condition variables and mutex locks.

<2>, C++ implements multi-threaded synchronization processing: controlling the output sequence of ABC, outputting 10 groups, mutex + condition_variable-CSDN Blog

6. Output result display:

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Algorithm skill tree Home page Overview 57615 people are learning the system