1, Thread theory foundation
Advantages of threads
One of the reasons to use multithreading:
Compared with the process, it is a very frugal way of multitasking. Under the Linux system, to start a new process, it must be allocated an independent address space, and a large number of data tables must be established to maintain its code segments, stack segments and data segments
A process has multiple threads, and the switching time between threads is very small. The overhead of a process is 30 times that of a thread
The second reason to use multithreading:
Convenient communication mechanism between threads. Threads in the same process share data space, so the data of one thread can be directly used by other threads (except local variables and formal parameters). The process has an independent data space, and the data transmission can only be carried out through inter process communication
Other reasons:
Make the multi cpu system (multi-core) more effective. The operating system will ensure that different threads run on different CPUs when the number of threads is not greater than the number of CPUs
Improve the program structure. A long and complex process can be divided into multiple threads and become several independent or semi independent running parts. Such a program will be conducive to understanding and modification
Required header file
Multithreading under Linux system follows POSIX thread interface, which is called pthread. The header file pthread is required to write multithreading program under Linux h. Libpthread is required for connection h
You need to add - lpthread when compiling gcc
2, Multithreaded programming
establish
#include<pthread.h> int pthread_create(pthread_t *tidp,const pthread_attr_t* attr,void*(*start_rtn)(void),void *arg)
Parameters:
tidp: thread id, the incoming address is an address (output)
attr: thread attribute (usually NULL)
start_rtn: the function to be executed by the thread, which is of type (void *)
arg:start_rtn parameter (void *) type, NULL if none
The return value is 0 successfully
sign out
If exit is invoked in any thread in the process, or Exit, then the whole process will stop. The normal exit modes of threads are:
(1) Thread returned from Startup
(2) A thread can be terminated by another process
(3) The thread itself can call pthread_exit function terminated
pthread_exit function
#include<pthread.h> void pthread_exit(void *rval_ptr)
Function: terminate calling thread
Rval_ptr: pointer to the return value of thread exit
Thread waiting
int pthread_join(pthread_t tid,void **rval_ptr);
Function: block the calling thread until the specified thread terminates
tid: thread id waiting to be pushed
rval_ptr: pointer (secondary pointer) of the return value of the thread exit. If there is no pointer, it is NULL
The return value is 0, failed; Otherwise, return value
3, Thread synchronization
Multithreaded programming, because it is impossible to know which thread will operate on the shared resources at which time, so how to protect the shared resources becomes complex
Resource competition between threads:
1 mutex
2 signal lamp
3 conditional variables
mutex
establish
Mutex cannot be defined in the main function, but should be defined globally
The mutex usage type is pthread_mutex_t indicates. It needs to be initialized before use;
For statically allocated mutex, you can set it as the default mutex object PTHREAD_MUTEX_INITIALLIZER
For dynamically allocated mutexes, after applying for memory (malloc), pthread is used_ mutex_ Init initializes, and pthread needs to be called before free ing memory_ mutex_ destroy
initialization
#include<pthread.h> int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr); int pthread_mutex_destroy(pthread_mutex_t *mutex);
Lock
For access to shared resources, the mutex should be locked. If the mutex is locked, the calling thread will block until the mutex is unlocked.
int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex);
Return value: 0 for success and error number for error.
Trylock is a non blocking call mode. If the mutex is not locked, the trylock function will lock the mutex and obtain access to shared resources; If the mutex is locked, the trylock function will not block the wait and directly return EBUSY, indicating that the shared resource is busy
Unlock
After the operation is completed, the mutex must be unlocked, that is, the release mentioned above. In this way, other threads waiting for the lock will have the chance to obtain the lock, otherwise other threads will block forever.
int pthread_mutex_unlock(pthread_mutex_t *mutex)
Destroy
pthread_mutex_destroy(pthread_mutex_t *mutex);
Conditional variable
establish
Define variable pthread_cond_t cond;
Conditional variable initialization
a,initiate static PTHREAD_COND_INITIALIZER b,dynamic initialization int pthread_cond_init(pthread_cond_t * cond,pthread_condattr_tond_attr);
Conditional variable undo
int pthread_cond_destroy(pthread_cond_t *cond);
Conditional variable wait
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
Send signal
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
Process: Unlock - > wait - > lock
Thread 1
pthread_mutex_lock(&mutex); if//(the condition is not tenable) pthread_cond_wait(&cond,*mutex); //modify condition pthread_mutex_unlock(&mutex);
Thread 2
pthread_mutex_lock(&mutex);
//Make the conditions meet and send the signal
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
4, Producer consumer issues
//Simulate producers producing bread and consumers eating bread //Create two processes, one for production and one for consumption //Use a circular queue to store bread //When the producer produces bread, the thread is locked and the consumer cannot eat bread. After production, the thread is unlocked and the consumer can eat bread. When the production of bread is enough (the team is full), the producer does not produce and waits //When enough bread is produced (the queue is full), the consumer eats bread. At this time, the thread is locked and the producer cannot produce until the bread is eaten (the queue is empty) #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<pthread.h> #include<errno.h> #define BUFFSIZE 16 #define OVER -1 struct procon { int buffer[BUFFSIZE];//Store bread, up to 16 int front,rear;//Circular queue pointer, used to judge empty and full pthread_mutex_t mutex;//mutex pthread_cond_t noempty;//Condition variable, not null pthread_cond_t nofull;//Conditional variable, dissatisfaction }; typedef struct procon P; P buf;//Variables that define structure types void init_all(P *buf)//initialization { pthread_mutex_init(&buf->mutex,NULL); pthread_cond_init(&buf->noempty,NULL); pthread_cond_init(&buf->nofull,NULL); buf->front = buf->rear = 0; } void put_in(P *buf,int data) { //Lock pthread_mutex_lock(&buf->mutex); //When the sentence is full if ((buf->rear + 1) % BUFFSIZE == buf->front) {//Release the lock and wait for another thread to give a signal that it is not full. After being awakened, it will take back the lock pthread_cond_wait(&buf->nofull,&buf->mutex); } //When it's not full, put the bread on the shelf buf->buffer[buf->rear] = data; buf->rear = (buf->rear + 1) % BUFFSIZE; pthread_cond_signal(&buf->noempty); //Unlock pthread_mutex_unlock(&buf->mutex); } int get_from(P *buf) { int data; pthread_mutex_lock(&buf->mutex); //When empty if (buf->front == buf->rear) { pthread_cond_wait(&buf->noempty,&buf->mutex); } //When not empty data = buf->buffer[buf->front]; buf->front = (buf->front + 1) % BUFFSIZE; pthread_cond_signal(&buf->nofull); pthread_mutex_unlock(&buf->mutex); return data; } void *produce_tid(void) { int i; //The production team produces only 20 pieces of bread for ( i = 0; i < 20; i++) { sleep(rand()%3);//Random production time printf("%d.one bread is producted\n",i + 1); put_in(&buf,i + 1); } put_in(&buf,OVER); return NULL; } void *contumer_tid(void) { int d; while (1) { sleep(rand()%3);//Eat bread at random d = get_from(&buf); if (d == OVER) { break; } printf("%d.eat a bread\n",d); } return NULL; } int main() { pthread_t tid_p,tid_c;//Create two thread producers and consumers init_all(&buf);//Call the function to initialize the mutex and condition variables //Create thread pthread_create(&tid_p,NULL,(void *)produce_tid,NULL); pthread_create(&tid_c,NULL,(void *)contumer_tid,NULL); //Wait for the thread to end pthread_join(tid_c,NULL); pthread_join(tid_p,NULL); return 0; }