当前位置:网站首页>Learn about threads
Learn about threads
2022-06-30 04:25:00 【Tra220123】
One . The concept of thread
In some cases, multiple control processes need to be executed simultaneously in one process , For example, to achieve a graphical interface to download software , On the one hand, it needs to interact with users , Wait for and process the user's mouse and keyboard events , On the other hand, you need to download multiple files at the same time , Wait and process data from multiple network hosts , These tasks all require a “ wait for -> Handle ” The cycle of , So how can I multitask at the same time ?
1. You need threads :
It is the smallest unit that the operating system can schedule operations . It is included in the process , Is the actual operation unit in the process . A thread refers to a single sequence of control flows in a process , Multiple threads can be concurrent in one process , Each thread performs different tasks in parallel .
2. Because multiple threads of the same process share the same address space , So code block , Data blocks are shared , If you define a function , You can call , If you define a global variable , In each thread, you can access , besides , Each thread also shares the following process resources and environment :
① Document descriptor table
② How each signal is processed
③ Current working directory
④ user id And groups id
But some resources are shared by each thread :
① Threads id
② Context , Including the values of various registers , Program counter and stack pointer
③ Stack space
④errno Variable
⑤ Signal mask word
⑥ Scheduling priority
stay Linux The upper thread function is for libpthread Shared library , Add... When compiling -lpthread
Two . Thread control
1. Create thread

Return value : Successfully returns 0, Failure returns error number . All the system functions that I have learned before all return successfully 0, Failure to return -1, The error number is saved in the global variable errno in .
pthread Library functions return error numbers by return values , Although each thread also has one errno, But this is provided for compatibility with other function interfaces ,pthread The library itself does not apply to it , It is better to return the error code through the return value .
2. Get the... Of the current thread id

Return value : Always return successfully , Returns the thread that called the function ID
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void *thr_fn(void *arg) {
printf("%s\n", arg);
return NULL;
}
void printid(char *tip) {
pid_t pid = getpid();
pthread_t tid = pthread_self();
printf("%s pid:%u tid: %u (%p)\n", tip, pid, tid, tid);
//printf("%s thr_fn=%p\n", tip, thr_fn);
return;
}
int main(void) {
pthread_t ntid;
int ret = pthread_create(&ntid, NULL, thr_fn, "new thread");
if (ret) {
printf("create thread err:%s\n", strerror(ret));
exit(1);
}
//sleep(1);
printid("main thread\n");
return 0;
}3. reflection : The main thread is in a global variable ntid For the newly created site id, If the newly created thread does not call pthread_self Instead, print this directly ntid, Can you achieve the same effect ?
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
pthread_t ntid;
void *thr_fn(void *arg) {
printid(arg);
printf("%s ntid=%p\n", arg, ntid);
return NULL;
}
void printid(char *tip) {
pid_t pid = getpid();
pthread_t tid = pthread_self();
printf("%s pid:%u tid: %u (%p)\n", tip, pid, tid, tid);
return;
}
int main(void) {
int ret = pthread_create(&ntid, NULL, thr_fn, "new thread");
if (ret) {
printf("create thread err:%s\n", strerror(ret));
exit(1);
}
sleep(1);
printid("main thread\n");
return 0;
}
4. If you need to terminate only one thread and not the entire process , There are three ways ?
① From the thread function return. This method is not applicable to the main thread , from main function return Equivalent to calling exit.
② A thread can call pthread_cancel Terminate another thread in the same process .
③ Threads can call pthread_exit Stop yourself .

value_ptr yes void* type , It is the same as the return value of thread function , Other threads can call pthread_join Get this pointer .
Be careful :pthread_exit perhaps return Returns the memory unit pointed to must Be global or use malloc The distribution of , Cannot allocate on the stack of thread functions ( Because when other threads get this return pointer , The thread function has exited )
5. The thread that calls the function will hang and wait , know id by thread Thread termination of .
thread Threads terminate in different ways , adopt pthread_join The end state you get is different , as follows :
① If thread Threads pass through return return ,value_ptr What is stored in the unit pointed to is thread The return value of the thread function .
② If thread Thread is called by another thread pthread_cancel Abort ,value_ptr The cell that you point to is a constant PTHREAD_CANCLED.
③ If thread The thread calls itself pthread_exit Terminated ,value_ptr The unit to which you point is passed to pthread_exit Parameters of .
④ If the thread The termination state of the thread is not interested , It can be transmitted NULL to value_ptr Parameters .

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
void *thr_fn1(void *arg) {
printf("thread1 returning\n");
return (void *)1;
}
void *thr_fn2(void *arg) {
printf("thread2 exiting\n");
pthread_exit((void *)2);
return NULL;
}
void *thr_fn3(void *arg) {
while (1) {
printf("thread3 is sleeping\n");
sleep(1);
}
return NULL;
}
int main() {
pthread_t tid;
void *sts;
pthread_create(&tid, NULL, thr_fn1, NULL);
pthread_join(tid, &sts);
printf("thread1 exit code: %ld\n", (long)sts);
pthread_create(&tid, NULL, thr_fn2, NULL);
pthread_join(tid, &sts);
printf("thread2 exit code: %ld\n", (long)sts);
pthread_create(&tid, NULL, thr_fn3, NULL);
sleep(3);
pthread_cancel(tid);
pthread_join(tid, &sts);
printf("thread3 exit code: %ld\n", (long)sts);
return 0;
}
3、 ... and . Synchronization between threads
1. There may be conflicts when multiple threads access shared data at the same time , The same problem as reentrancy .
For example, both threads need to add a global variable 1, This slave operation needs three instructions to complete on a certain platform :
Read variable values from memory to registers --> Register value plus 1--> Write the value of the register back to memory
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int cnt = 0;
void *cntadd(void *arg) {
for (int i = 0; i < 5000; i++) {
int val = cnt;
printf("%p: %d\n", pthread_self(), val);
cnt = val+1;
}
return NULL;
}
int main(void) {
pthread_t tida, tidb;
pthread_create(&tida, NULL, cntadd, NULL);
pthread_create(&tidb, NULL, cntadd, NULL);
pthread_join(tida, NULL);
pthread_join(tidb, NULL);
return 0;
}2. For multithreaded programs , The problem of access conflicts is common , The solution is to introduce mutexes :Mutex(Mutual Exclusive Lock)
The thread that obtains the lock can complete “ read - modify - Write ” The operation of , Then release the lock to other threads , Threads that do not acquire locks can only wait and cannot access shared data , such “ read - modify - Write ” Three steps make up an atomic operation , Or both , Either not , It will not be interrupted in the middle , It will not do this operation in parallel on other processors .

pthread_mutex_init Function pair mutex Do initialization , Parameters attr Set up mutex attribute , If attr by NULL Represents the default property .
use pthread_mutex_init Function initialization mutex It can be used pthread_mutex_destrory The destruction .
If mutex Variables are statically assigned ( Global variables or static Variable ), You can also use macros to define PTHREAD_MUTEX_INITIALIZER To initialize the , Equivalent to using pthread_mutex_init Initialize and attr Parameter is NULL.
3.mutex The following functions can be used to lock and unlock :

Return value : Successfully returns 0, Failure returns error number .
A thread can call pthread_mutex_lock get mutex, If another thread has already called pthread_mutex_lock To obtain the mutex, Then the current thread needs to hang and wait , Know that another thread calls pthread_mutex_unlock Release mutex, The current thread is awakened , To get the mutex And continue .
If a thread wants to acquire a lock , I don't want to hang up waiting , You can call pthread_mutex_trylock, If mutex Has been obtained by another thread , This function will fail and return EBUSY, It does not suspend the thread from waiting .
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int cnt = 0;
pthread_mutex_t add_lock = PTHREAD_MUTEX_INITIALIZER;
void *cntadd(void *arg) {
for (int i = 0; i < 5000; i++) {
pthread_mutex_lock(&add_lock);
int val = cnt;
printf("%p: %d\n", pthread_self(), val);
cnt = val+1;
pthread_mutex_unlock(&add_lock);
}
return NULL;
}
int main(void) {
pthread_t tida, tidb;
pthread_create(&tida, NULL, cntadd, NULL);
pthread_create(&tidb, NULL, cntadd, NULL);
pthread_join(tida, NULL);
pthread_join(tidb, NULL);
return 0;
}4. How to implement the operations of suspending the waiting thread and waking the waiting thread ?
Every mutex There is a waiting queue , A thread should be in mutex Pending on , First, add yourself to the waiting queue , Then set the thread status to sleep , Then call the scheduler function to switch to another thread . A thread wants to wake up waiting for other threads in the queue , Just take one item from the waiting queue , Change its state from sleep to ready , Join the ready queue , Then the next time the scheduler function executes, it is possible to switch to the awakened thread .
If the same thread calls twice lock, On the second call , Because the lock has been occupied , The thread will hang and wait for another thread to release the lock , However, the lock is occupied by itself , The thread was suspended again without a chance to release the lock , So it is always in a pending state , It's called Deadlock (deadlock).
Another deadlock situation : Threads A Gets the lock 1, Threads B Gets the lock 2, When a thread A call lock Trying to get a lock 2, The result is that the waiting thread needs to be suspended B Release the lock 2, And then the thread B Also call lock Trying to get a lock 1, As a result, the waiting thread needs to be suspended A Release the lock 1, So thread A and B Are always suspended . If more threads and more locks are involved , Whether there is a possibility of deadlock will become complex and difficult to judge .
When writing programs, try to Avoid acquiring multiple locks at the same time , If it must be necessary , One principle :
If all threads need multiple locks Obtain locks in the same order , There will be no deadlock . For example, a program uses a lock 1, lock 2, lock 3, They correspond to mutex Variables are locks 1-> lock 2-> lock 3, Then all threads need to obtain at the same time 2 Or 3 All locks should be pressed 1, lock 2, lock 3 Get... In the order of .
It is difficult to set a priority for all locks , Try to use pthread_mutex_trylock Call substitution pthread_mutex_lock call , To avoid deadlock .
5. There is another case of synchronization between threads : Threads A You have to wait for something to be true before you can proceed , Now this condition doesn't hold , Threads A Just block and wait , And threads B Make this condition true during execution , Just wake up the thread A Carry on .
stay pthread In the library, a condition variable is used to block waiting for a condition , Or wake up the thread waiting for this condition . Conditional variable pthread_cond_t Type to represent , You can initialize and destroy :

Return value : Successfully returns 0, Failure returns error number .
6. The operation of conditional variables can be performed with the following functions :

Return value : Successfully returns 0, Failure returns error number .
pthread_cond_timedwait The function also has an additional parameter to set the wait timeout , If arrive abstime There is still no other thread to wake up the current thread at the specified time , Just go back to TIMEDOUT, A thread can call pthread_cond_signal Wake up waiting for another thread on a condition variable , You can also call pthread_cond_broadcast Wake up all threads waiting on this condition variable .
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
typedef struct Goods
{
int data;
struct Goods *next;
} Goods;
Goods *head = NULL;
pthread_mutex_t headlock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t hasGoods = PTHREAD_COND_INITIALIZER;
void *producer(void *arg) {
Goods *ng;
while (1) {
ng = (Goods *)malloc(sizeof(Goods));
ng->data = rand() % 100;
pthread_mutex_lock(&headlock);
ng->next = head;
head = ng;
pthread_mutex_unlock(&headlock);
pthread_cond_signal(&hasGoods);
printf("produce %d\n", ng->data);
sleep(rand() % 3);
}
}
void *consumer(void *arg) {
Goods *k;
while (1) {
pthread_mutex_lock(&headlock);
if (!head) {
pthread_cond_wait(&hasGoods, &headlock);
}
k = head;
head = head->next;
pthread_mutex_unlock(&headlock);
printf("consume %d\n", k->data);
free(k);
sleep(rand() % 3);
}
}
int main(void) {
srand(time(NULL));
pthread_t pid, cid;
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&pid, NULL, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}7.mutex Variables are not 0 namely 1, As a resource available quantity , On initialization mutex yes 1, Indicates that there is a resource available , Obtain the resource when locking , take mutex Reduced to 0, Indicates that there are no more resources available , Release the resource when unlocking , take mutex Add back to 1, Indicates that there is another available resource .
8. Semaphore semaphore and mutex similar , Indicates the number of available resources , and mutex The difference is : This amount can be greater than 1, This semaphore is not only used to agree the synchronization between threads of a process , It can also be used for synchronization between different processes .

semaphore The variable type is sem_t
sem_init() Initialize the session semaphore Variable ,value The parameter indicates the number of available resources ,pshared Parameter is 0 Indicates that semaphores are used for synchronization between threads of the same process .
After using up semaphore Variable should be called after sem_destroy() Release and semaphore Related resources .
call sem_wait() Resources available , Use semaphore Value reduction 1, If the sem_wait() when semaphore The value of is already 0, Suspend waiting . If you don't want to hang and wait , You can call sem_trywait().
call sem_post() Can release resources , send semaphore It's worth adding 1, At the same time, wake up the pending thread .
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define NUM 5
int q[NUM];
sem_t blank_number, goods_number;
int head, tail;
void *producer(void *arg) {
while (1) {
sem_wait(&blank_number);
q[tail] = rand() % 100 + 1;
printf("produce %d\n", q[tail]);
sem_post(&goods_number);
tail = (tail + 1) % NUM;
sleep(rand() % 3);
}
}
void *consumer(void *arg) {
while (1) {
sem_wait(&goods_number);
printf("consume %d\n", q[head]);
q[head] = 0;
sem_post(&blank_number);
head = (head + 1) % NUM;
sleep(rand() % 3);
}
}
int main() {
srand(time(NULL));
sem_init(&blank_number, 0, NUM);
sem_init(&goods_number, 0, 0);
pthread_t pid, cid1, cid2, cid3;
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid1, NULL, consumer, NULL);
pthread_create(&cid2, NULL, consumer, NULL);
pthread_create(&cid3, NULL, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid1, NULL);
pthread_join(cid2, NULL);
pthread_join(cid3, NULL);
return 0;
}边栏推荐
- JS deconstruction assignment
- An error occurs when sqlyog imports the database. Please help solve it!
- JS static method
- FortiGate firewall configuration link detection link monitor and status query
- The same node code will cause duplicate data
- SQL error caused by entity class: Oracle "ora-00904" error: possible case of invalid identifier
- How the FortiGate firewall rejects a port by using the local in policy policy
- 基于servlet+jsp+mysql实现的工资管理系统【源码+数据库】
- Salary management system based on servlet+jsp+mysql [source code + database]
- El upload upload file (manual upload, automatic upload, upload progress)
猜你喜欢

Junior students summarize JS basic interview questions

OneNote software

iMile 利用 Zadig 多云环境周部署千次,跨云跨地域持续交付全球业务

Troubleshoot abnormal video playback problems in public network deployment based on Haikang ehomedemo tool

lego_ Reading and summary of loam code

Grasp grpc communication framework in simple terms

Machine learning notes

进程间通信之匿名管道

el-upload上传文件(手动上传,自动上传,上传进度)

AI落地的新范式,就“藏”在下一场软件基础设施的重大升级里
随机推荐
JS inheritance
Slam mapping, automatic navigation and obstacle avoidance based on ROS (bingda robot)
Share an example of a simple MapReduce method using a virtual machine
FortiGate firewall and Aruze cloud tunnel interruption
SQL server2005中SUM函数中条件筛选(IF)语法报错
Qt6 QML Book/Qt Quick 3D/Qt Quick 3D
Named pipes for interprocess communication
Day 10 data saving and loading
If you encounter problems when using spark for the first time, please ask for help
Indefinite parameters of JS function
工程安全和工程质量
JS file block to Base64 text
El upload Upload file (Manual upload, Automatic upload, upload progress)
Junior students summarize JS basic interview questions
An error occurs when sqlyog imports the database. Please help solve it!
Huawei cloud native - data development and datafactory
Tea mall system based on SSM framework [project source code + database script + report]
两个月拿到N个offer,什么难搞的面试官在我这里都不算事
FortiGate firewall configuration log uploading regularly
Introduction to cloud native + container concept