The interview points are coming~
Article Directory
foreword
In the principle of mutex in the previous article, we explained the principle of locks. We know that every time a thread applies for a lock, once the application is successful, the thread itself takes the lock with itself, which ensures the atomicity of the lock (because There is only one lock), and what happens when we have successfully applied for the lock and then apply for the lock? Below we answer this question in deadlock.
1. Deadlock
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *threadRoutine(void* args)
{
cout<<"我是一个新线程"<<endl;
pthread_mutex_lock(&mutex);
cout<<"我拿到了锁"<<endl;
pthread_mutex_lock(&mutex);//由于再次申请锁的问题会停下来(死锁)
cout<<"我又活了过来"<<endl;
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,threadRoutine,nullptr);
sleep(3);
cout<<"主线程正在运行"<<endl;
return 0;
}
First of all, let's not let the main thread release the lock, and look at the state of the deadlock:
We can see that "I am alive again" should have been printed, but it exited directly. Let's let the main thread unlock the new thread:
Through the running results, we can see that the deadlocked process has come alive, which means that one thread can apply for a lock and another thread can release the lock. Therefore, it is necessary to destroy the fourth condition to directly control a thread to release the lock uniformly.
Here we summarize:
const int num = 5;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *active(void* args)
{
string name = static_cast<const char*>(args);
while (true)
{
pthread_mutex_lock(&mutex); //先加锁
//然后放进条件变量去等
pthread_cond_wait(&cond,&mutex); //调用的时候会自动释放锁
cout<<name<<" 活动 "<<endl;
pthread_mutex_unlock(&mutex);
}
}
int main()
{
pthread_t tids[num];
for (int i = 0;i<num;i++)
{
char* name = new char[32];
snprintf(name,32,"thread -> %d ",i+1);
pthread_create(tids+i,nullptr,active,name);
}
sleep(3);
while (true)
{
cout<<"主线程唤醒其他线程......"<<endl;
//在环境变量中按顺序唤醒一个线程
pthread_cond_signal(&cond);
sleep(1);
}
for (int i = 0;i<num;i++)
{
pthread_join(tids[i],nullptr);
}
return 0;
}
The function of the above code is: first create 5 threads, these 5 threads must enter the active function, first add a lock in the function, and then let this thread wait in the environment variable, and release the lock by itself during the waiting process, Then after 3 seconds, the main thread starts to wake up the threads in these environment variables. The function of the pthread_cond_signal interface is to wake up one thread at a time. Each wakeup is woken up in order. After waking up, the thread will print "activity", as follows Let's run it and see:
You can see that the sequence of wakeups here is 31245. Next, we can use the pthread_cond_broadcast interface to wake up all interfaces at once:
Through the running results, we can see that the wake-up is also performed in order, so the function of the condition variable is to allow multiple threads to wait in queue in cond (queue waiting is a kind of order).
Here we summarize:
2. Producer-consumer model
Let's first draw a picture to understand the producer-consumer model:
First of all, in the model, the supermarket does not belong to the producer. The supermarket is just a trading place. The supplier will put the produced goods in the supermarket, and then the consumer will go directly to the supermarket to buy. Why don't consumers go directly to the supplier to buy? Because consumers don't know when the suppliers will produce the goods, and the demand of consumers is very fragmented, and the production level of suppliers is very strong, resulting in a natural barrier between the two. So what are the characteristics of this model?
The first point is high efficiency, and the second point is uneven busyness. What does the second point mean? It means that we can put a large number of commodities produced by suppliers in the supermarket for consumers to buy anytime and anywhere. When suppliers are busy producing commodities, when the commodities produced have not been purchased or there are still many When the goods are stored, the supplier will not produce too much, and this time is idle. Now let's make the model concrete, the consumer is actually a thread, the producer (supplier) is also a thread, and the supermarket, the trading place, is actually a specific buffer (queue, chain, hash, etc.), The commodities in the supermarket above are actually data. What do you think of this model? That’s right, it’s pipeline communication. Isn’t a pipeline just that one process puts data in the buffer and another process takes it from the buffer? We wrote an example of code that closed the read end and closed the write end. I don’t know if you remember it.
For the buffer we just mentioned, this buffer must be seen by both consumers and producers, so it is destined that the trading place must be a public area that will be accessed concurrently by multiple threads, and it is destined to be multi-threaded. Threads must protect the security of shared resources, and it is doomed to maintain the relationship between thread mutual exclusion and synchronization in this case.
Let's explain the relationship in the model:
1. Producer and producer: There must be a mutually exclusive relationship between producers and producers, because they are all rushing to store resources in the buffer, only whoever produces faster will put them into the buffer first.
2. Consumers and consumers: The relationship between consumers and consumers must also be mutually exclusive , because when there is only one product, then two consumers will definitely grab this product, which reflects mutuality at this time. Rejection.
3. Producers and consumers: There is a synchronous relationship between producers and consumers . Because when the product is produced, the producer will continue to produce only after the consumer consumes it, otherwise the buffer is full, even if the producer produces again, it will not be put into the buffer. There is a mutually exclusive relationship between the second producer and the consumer , because we cannot let the consumer send things to the buffer while taking things in the buffer, there can only be one between the producer and the consumer into the buffer.
Let's record the producer-consumer model in a simple way:
3 relationships (producer and producer, consumer and consumer, producer and consumer), two roles (producer and consumer), one transaction place (usually a buffer). So we only need to remember 321 for the producer-consumer model in the future.