Thread creation and inter-thread communication (C language)

Abstract: How threads are created, how communication between threads is achieved, what needs to be paid attention to when communicating between threads, how thread synchronization and mutual exclusion use critical resources, today is another day for us to study hard together ,Lets come look.

        What is a thread? Yesterday we learned about processes. It is said that the address space of each process is independent of each other, and each process has a task_struck. During process switching, the cache cache needs to be refreshed continuously, which consumes resources. In order to reduce this consumption, a lightweight process-thread is introduced.

        Thread characteristics: Multiple threads created by the same process share the address space of the same process. When a process creates a thread, the original process is also called a thread and becomes the main thread.

        So how are threads created? Let's first look at the function that creates a thread:

#include <pthread.h>

int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);

At first glance, this function is so complicated that it has four parameters. It doesn't matter, let's look at them one by one.

① thread This parameter represents the thread object, and each thread corresponds to a thread object.

It must be defined before using it. Looking at its type, it can be seen that it is of type pthread_t.

② attr This is the thread attribute, and the default attribute is NULL by default.

③ void *(*start_routine)(void *) This thing looks complicated, but it is actually filling in the function name, and the thread is encapsulated by a function, so the function name filled in is the entry point of our thread.

④ arg This is to fill in the parameters that need to be passed to the thread. Only one parameter can be passed at a time. If you want to pass multiple parameters, you can define a structure first, pass the structure name, and fill in NULL if you don’t want to pass parameters.

        It can be seen from the function that the return value is of int type. If the thread is successfully created, it will return 0, and if it fails, it will return 1, which can be used to determine whether the thread is successfully created.

         Like a process, when there is a creation, there will naturally be an end thread and a waiting thread. Let's first look at the function that ends the thread:

#include <pthread.h>

void pthread_exit(void *reatval);

        Just reatval is a parameter. This parameter is filled with the information returned when the thread ends, which is received by the waiting thread function. If you do not want to return information, you can fill in NULL.

        Waiting thread: It is the operation that the main thread needs to perform, because the thread shares the same address space of the main thread, so if the main thread ends first, then the sub-thread also ends immediately. So if you want the main thread to end behind the child thread, you must have this waiting function. The function is as follows:

#include <pthread.h>

int pthread_join(pthread_t thread,void **retval);

Parameters: ① thread The thread object that needs to wait

            ②retval can be seen that it is a secondary pointer, and the secondary pointer needs to pass the primary pointer, and it is of void type. It is used to receive the content returned by the end thread function, which can be NULL.

Now that the functions are known, let's start creating threads:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
void *func(); //初始化两个函数 用于建立两个线程
void *func1();
struct m_art  //定义一个结构体 用于传递需要传递的参数
{
    int a;
    char b;
};
int main(int argc, char *argv[]) 
{
    pthread_t thread;//定义两个线程对象
    pthread_t thread1;
    void *ch =NULL;  //定义两个retval用于装结束线程返回的内容
    void *ch1 =NULL;
    struct m_art t;  //定义相同类型的结构体变量名 并给成员赋值
    t.a=5;
    t.b='l';
    int n = pthread_create(&thread,NULL,func,&t); //创建线程第1个 传递结构体名&t
    if(n<0)        //判断线程是否创建成功
    {
        perror("pthread_create1");
        exit(-1);
    }
    int m = pthread_create(&thread1,NULL,func1,NULL);  //创建线程第2个 不传递参数 填NULL。
    if(m<0)             //判断线程是否创建成功
    {
        perror("pthread_create2");
        exit(-1);
    }
    // 以下为主线程

    pthread_join(thread,&ch);    //等待线程。
    printf("%s \n",(char *)ch);  //打印线程1的返回内容
    pthread_join(thread1,&ch1);
    printf("%s \n",(char *)ch1);
    return 0;
}
void *func(void *arg) //线程1
{       
        int i=10;
        struct m_art *t=(struct m_art*)arg;  //定义相同类型结构体指针接收结构体名
        while(i--)
        {   
            if(i==4)  //提前结束线程条件
            {
                pthread_exit("pt");  //结束时返回"pt"
            }

            printf("mmmmmmmmmmmmm  %d  %c\n",t->a,t->b); 打印查看通过传递结构体传递的参数
            sleep(1);  //每隔一秒执行一次 便于观察
        }
}

void *func1() //线程2.因不传递参数,可不填参数
{
    printf("ccccccccccc\n");
    pthread_exit("pt1");  //结束时返回"pt1"
}

        Next, we need to implement inter-thread communication, because they share an address space, so inter-thread communication can be implemented through global variables. Because it is implemented through global variables, when a thread uses the global variable, other threads are also accessing the data, so some threads will be destroyed. So we can solve this problem through thread synchronization and thread mutual exclusion.

        What is synchronization and mutual exclusion, how to synchronize between threads or mutual exclusion between threads?

Synchronization: Between multiple threads, programs are executed successively in the agreed order.

In synchronization, we have something called a semaphore. Each thread must have a semaphore if it wants to execute the corresponding program. If the semaphore is 0, it will enter the blocking state. The operation semaphore must be accessed through a specific function interface, and operators cannot be used to directly operate. A semaphore is essentially a non-negative integer.

Initialize the semaphore:

#include <semaphore.h>

int sem_init(set_t *sem,int pshared,unsigned int value);

parameter:

        ① sem semaphore, similar to thread object, needs to define set_t type.

        ② pshared is used for inter-thread synchronization and must be filled with 0.

        ③ value The initial value of the semaphore.

 When the thread is running, the semaphore changes as needed, so the semaphore application is released.

Semaphore application (P operation):

#include <semaphore.h>

int sem_wait(set_t *sem);

        Call once to apply for a data volume.

Semaphore release (V operation):

#include <semaphore.h>

int sem_post(set_t *sem);

         Call once to release a data volume

Next, let's see how to synchronize threads:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
void *func();
void *func1();
char buf[64]={0}; //定义全局变量 buf缓冲区
sem_t sem1,sem2;  //定义两个信号量

int main(int argc, char *argv[])
{
    pthread_t thread;  //定义线程对象
    pthread_t thread1;
    sem_init(&sem1,0,0); //初始化信号量 资源为0
    sem_init(&sem2,0,1); //初始化信号量 资源为1
    int n = pthread_create(&thread,NULL,func,NULL);  //创建线程1
    if(n<0)
    {
        perror("pthread_create1");
        exit(-1);
    }
    int m = pthread_create(&thread1,NULL,func1,NULL); //创建线程2
    if(m<0)
    {
        perror("pthread_create2");
        exit(-1);
    }
    pthread_join(thread,NULL); //等待线程
    pthread_join(thread1,NULL);
    return 0;
}
void *func()  //线程1
{       
    while(1)
    {
        sem_wait(&sem1); //等待信号量(资源)一开始有0个,进入阻塞状态,去执行下一个线程,直到信号量sem1有资源。
        fputs(buf,stdout); //打印全局变量buf缓冲区里的内容;
        sem_post(&sem2); //给sem2申请信号量 让其变为1。
    }
}

void *func1()  /线程2
{
    while(1)
    {
        sem_wait(&sem2); //等待信号量(资源)一开始有1个,执行此线程,然后sem2信号量0.
        fgets(buf,64,stdin); //等待输入
        sem_post(&sem1);  //给sem1申请信号量,让其变为1。
    }
}

In this way, we can achieve inter-thread communication.

Mutual exclusion: When one thread uses shared data, no other thread can use the data.

        Data that multiple threads can access together is called a critical resource. A code block involving critical resources is called a critical section, and mutual exclusion is to protect the critical section with a mutex.

There are also three functions to synchronize threads:

① Mutex initialization

#include <pthread.h>

int pthread_mutex_init(pathread_mutex_t *mutex,pthread_mutex_t *attr);

parameter:

         mutex--Mutual exclusion lock, defined as semaphore object and thread object.

          attr - fill in NULL.

return value:

          Returns 0 on success and -1 on failure.

② Apply for lock (lock):

#include <pthread.h>

int pthread_mutex_lock(pathread_mutex_t *mutex);

③ Release the lock (unlock):

#include <pthread.h>

int pthread_mutex_unlock(pathread_mutex_t *mutex);

Next, let's see how to make threads mutually exclusive:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
void *func();                //大部分同上 相同部分不做过多解释
void *func1();
int num1=0,num2=0,sum=0;
pthread_mutex_t mutex;    //定义锁
int main(int argc, char *argv[])
{
    pthread_t thread;
    pthread_t thread1;
    pthread_mutex_init(&mutex,NULL);    //初始化锁
    int n = pthread_create(&thread,NULL,func,NULL);
    if(n<0)
    {
        perror("pthread_create1");
        exit(-1);
    }
    int m = pthread_create(&thread1,NULL,func1,NULL);
    if(m<0)
    {
        perror("pthread_create2");
        exit(-1);
    }
    pthread_join(thread,NULL);
    pthread_join(thread1,NULL);
    return 0;
}
void *func()
{       
    while(1)
    {
        pthread_mutex_lock(&mutex); //上锁
        num1=sum;
        num2=sum; // 在此期间的数据据改变过程中 即使时间片结束,下一个线程也访问不到次期间的数据。
        sum++;
        pthread_mutex_unlock(&mutex); //解锁
    }
}

void *func1()
{
    while(1)
    {
        pthread_mutex_lock(&mutex);//上锁
        if(num1 != num2)  //按理来讲上个线程在第一句赋值之后有可能时间片在此结束,则导致num1和num2不同
//在此会打印,但由于上了锁,只能访问上一次的值,而上一次的值又是相等的,所以这里永远不会打印
        {
            printf("num1=%d  num2=%d\n",num1,num2);
        pthread_mutex_unlock(&mutex); //解锁
        }
    }
}

Okay, grandpa readers, this is the end of today’s sharing, please correct me if I am wrong, we will continue to study tomorrow, thank you for watching.

Guess you like

Origin blog.csdn.net/weixin_56187542/article/details/126251049