当前位置:网站首页>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;
}
边栏推荐
- Five methods to clear floating and their advantages and disadvantages
- I get n offers in two months. I don't have any difficult interviewers here
- Knowledge - how to build rapport in sales with 3 simple skills
- Intern method of string
- SQL追加字段
- Clients accessing the daytime service (TCP)
- MySQL updates JSON string in array form
- iMile 利用 Zadig 多云环境周部署千次,跨云跨地域持续交付全球业务
- Quick sort & merge sort
- 输入输出及中断技术——微机第六章学习笔记
猜你喜欢
el-upload上傳文件(手動上傳,自動上傳,上傳進度)
El upload upload file (manual upload, automatic upload, upload progress)
Configure specific source IP in SLA detection of FortiGate sdwan
深度融合云平台,对象存储界的“学霸”ObjectScale来了
El upload Upload file (Manual upload, Automatic upload, upload progress)
Junior students summarize JS advanced interview questions
Junior students summarize JS basic interview questions
Blocking queue example
FortiGate firewall configuration log uploading regularly
I spent three years in a big factory outsourcing, which subverted my understanding!
随机推荐
OneNote software
01 backpack, dynamic planning
AI落地的新范式,就“藏”在下一场软件基础设施的重大升级里
Unity 在编辑器中输入字符串时,转义字符的输入
Iterator of JS
MySQL DDL change
Maya Calendar(POJ1008)
Share an example of a simple MapReduce method using a virtual machine
7-3 打怪升级 单源最短路
SQL server2005中SUM函数中条件筛选(IF)语法报错
el-upload上传文件(手动上传,自动上传,上传进度)
Titanic(POJ2361)
Five methods to clear floating and their advantages and disadvantages
Thingsboard tutorial (II and III): calculating the temperature difference between two devices in a regular chain
internship:接口案例实现
base64.c
Myrpc version 0
After the win10 system uses the browser to download, the content is moved or deleted without reason
MySQL updates JSON string in array form
管道实现进程间通信之命名管道