linux线程及线程间通讯

目录

一、线程

1.线程接口相关函数

(1)创建线程

(2)结束线程

(3)等待线程

2.线程间通信

(1)同步

(2)互斥


一、线程

每一个进程的地址空间是相互独立的

每一个进程都有一个叫task_struct任务结构体

在进行进程切换时需要不断刷新cache缓存,比较消耗资源

为了减少cache刷新时的资源消耗,引入了轻量级进程,称为线程。

线程特点:

同一个进程创建的多个线程,共用同一个进程的地址空间

进程创建线程后,我们把原本进程也可以称为线程,称为主线程

进程被称为最小的资源分配单位

线程称为最小的任务调度单位

线程公共数据:

用户名、用户组名

静态数据、全局数据(线程通信时使用)

文件描述符

私有数据:

线程ID

pc

优先级、状态、属性

堆栈

1.线程接口相关函数

pthread_create() 创建线程

pthread_exit() 结束线程

pthread_join() 等待线程

(1)创建线程

#include

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
在编译与线程操作相关的程序时,需要连接线程库,线程库的库名    --pthread
Compile and link with -pthread.(要链接线程库)

参数:
    thread:线程对象,一个线程对应一个对象(唯一标识一个线程)
    attr:线程属性,分离属性,结合属性(属性默认选项填NULL,结合属性)
    start_routine(指针函数指针):线程处理函数,传入一个函数名
    arg:给线程处理函数start_routine传参(结构体指针),如果线程处理函数没有参数,填NULL
    
返回值:
    成功返回0,失败返回错误编号
    
eg:
void * func(void *);
int main()
{
    pthread_t thread1;
    char buf[]="bbbbbbb";
    int ret=pthread_create(&thread1,NULL,func(),buf);
    if(ret<0)
    {
        perror("pthread_create");
        
    }
    while(1)
    {
        printf("aaaaaaaaa\n");
        sleep(1);
    }
}
void *func(void *buf)
{
    while(1)
    {
        puts((char *)buf);
        sleep(1);
    }
    
}

(2)结束线程

#include
void pthread_exit(void *retval);
参数:
    retval:线程结束信息,由pthread_join等待接受,如果不想返回信息,则填空(给join提供返回值)
        
    当结束主线程时,利用pthread_exit()结束不了其内的线程,应该使用exit()来结束主线程

(3)等待线程

#include

int pthread_join(pthread_t thread, void **retval);
等待线程一般在主线程结尾
参数:
    thread:线程对象
    retbal:传入一级指针的地址

函数会阻塞程序,等到接收到制定线程对象的线程结束,如没有pthread_exit(),内的参数,则没有接收值。

void *buf=NULL;
pthread_join(thread1,&buf);
printf("%s\n",(char *)buf);/puts(buf);

2.线程间通信

线程间通信只需要利用全局变量就可以实现。

在一个线程使用全局变量时,有可能其他线程,也在访问该数据,那么某一线程使用的数据就可能遭到破坏。

通过线程的同步和互斥,能够达到数据保护的效果

同步可以规定多个线程的执行顺序,互斥不能规定线程的执行顺序,只能保证一个线程的执行过程中不会被其他线程打断

(1)同步

多个线程之间,按照事先约定好的顺序有先后的完成某个事件。

信号量:是系统中的一种资源,本质是一个非负整数,信号量的值等于资源个数 操作信号量只能通过特定函数接口访问:

sem_init() 资源数初始化为0

sem_wait() 资源数-1 有资源才用,没有就阻塞

sem_post() 资源数+1 唤醒阻塞的线程

sem_init()    --信号量的初始化

sem_wait()    --P操作(申请资源)
    if(是否有资源)
    {
        执行后续代码;
        信号量-1;
    }
    else
    {
        阻塞等待,直到有资源唤醒为止;
    }
    
sem_post()    --V操作(释放资源)
    信号量+1;
    if(有等待资源的程序)
    {
         将其唤醒;   
    }

#include

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

信号量定为全局变量,在主线程中初始化

在编译与线程操作相关的程序时,需要连接线程库,线程库的库名    --pthread


参数:sem:信号量对象,一个对象对应一个信号量,全局变量
         pshared:线程间同步填0
         value:信号量的初始值,一般为0
    
在编译与线程操作相关的程序时,需要连接线程库,线程库的库名    --pthread
    

#include
int sem_wait(sem_t *sem);

参数:
    sem:要操作的互斥锁对象
返回值:    
    成功返回0,失败返回错误号


#include
int sem_post(sem_t *sem); 

参数:
    sem:要操作的互斥锁对象
返回值:    
    成功返回0,失败返回错误号

练习:创建两个线程,一个线程从键盘获取数据,另一个线程打印输出。

/*===============================================
*   文件名称:communication.c
*   创 建 者:     
*   创建日期:2022年08月09日
*   描    述:
================================================*/
#include 
#include 
#include 
#include 
#include 

void *func1();
void *func2();
char buf[4]={0};//缓冲区,用于存储读取到的数据
sem_t sem;	//信号量1
sem_t sem1;	//信号量2

int main(int argc, char *argv[])
{ 
    pthread_t thread1,thread2;	//定义线程对象
    sem_init(&sem,0,0);//初值设为0
    sem_init(&sem1,0,1);//初值设为1,以便第一次输入
    
    int ret1=pthread_create(&thread1,NULL,func1,NULL);//创建线程1
    if(ret1<0)
    {
        perror("pthread_create");
        exit(-1);
    }
    int ret2=pthread_create(&thread2,NULL,func2,NULL);//创建线程1
    if(ret2<0)
    {
        perror("pthread_create");
        exit(-1);
    }
    pthread_join(thread1,NULL);	//阻塞主线程,等待
    pthread_join(thread2,NULL);
    return 0;
} 
void *func1()	//线程1的函数
{
    while(1)
    {
        //printf(">:");
        sem_wait(&sem1);    // 1 -1	信号量2的初值为1,可以执行一次fgets。
        fgets(buf,4,stdin);
        sem_post(&sem);//   0   +1	信号量1的初值为0,加一后可以输出一次
    }
    
}


void *func2()	//线程函数2
{
    while(1)
    {
        sem_wait(&sem); //  0   -1	信号量1初值为0,需等待fgets后的post加一之后才可以输出
        fputs(buf,stdout);
        sem_post(&sem1);//  1   +1	信号量2的初值为1,在fgets之前的wait时-1,
        				//这里的puts之后+1,以便进行下一次fgets	
    }
}

(2)互斥

当一个线程使用公共数据时,其他线程都不能访问该共享数据

临界资源:公共资源,多个线程能够共同访问的数据(全局变量)

临界区:涉及到临界资源的代码模块

互斥是使用互斥锁来保护临界区。

互斥锁的相关操作接口函数

1.互斥锁的初始化--pthread_mutex_init()

int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr *attr)

参数:互斥锁的对象,定义为全局变量,线程都能访问到
    属性,填NULL缺省属性
    
返回值:
    成功返回0,失败返回-1

2.申请锁--pthread_mutex_lock()

pthread_mutex_lock(pthread_mutex_t *mutex)


参数:
    mutex,要操作的互斥锁对象
返回值:    
    成功返回0,失败返回错误号

3.释放锁--pthread_mutex_unlock()

pthread_mutex_unlock(pthread_mutex_t *mutex)


参数:
    mutex,要操作的互斥锁对象
返回值:
     成功返回0,失败返回错误号

/*===============================================
*   文件名称:mutec.c
*   创 建 者:     
*   创建日期:2022年08月09日
*   描    述:
================================================*/
#include 
#include 
#include 

void *func();	//函数声明
int num1=0;	//全局变量
int num2=0;
int count=0;
pthread_mutex_t mutex;//互斥锁的对象

int main(int argc, char *argv[])
{ 
    pthread_t thread;	//线程的对象
    pthread_mutex_init(&mutex,NULL);	//互斥锁的初始化
    int ret=pthread_create(&thread,NULL,func,NULL);//线程的创建
    if(ret<0)
    {
        perror("pthread_create");
        exit(-1);
    }
    while(1)
    {
        pthread_mutex_lock(&mutex);	//加锁
        num1=count;
        num2=count;
        count++;
        pthread_mutex_unlock(&mutex);	//解锁
    }

    return 0;
} 

void *func()
{
    while(1)
    {

        pthread_mutex_lock(&mutex);	//加锁
        if(num1!=num2)
        {
            printf("num1=%d  num2=%d\n",num1,num2); 
        }
        pthread_mutex_unlock(&mutex);//解锁
    }
}

你可能感兴趣的:(IO进线程,linux,c语言,开发语言,linux,c语言)