栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > Java

线程的创建与线程间通信(C语言)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

线程的创建与线程间通信(C语言)

摘要:线程是如何创建的,线程之间的通信是如何做到的,线程之间通信需要注意什么,线程的同步与互斥是如何使用临界资源的,今天,又是我们一起努力学习的一天,一起来看看。

        什么是线程,昨天我们学习了进程,说到每个进程的地址空间都是相互独立的,每个进程都有一个stask_struck。在进行进程切换时,需要不断地刷新cache缓存,比较消耗资源,为了减少这种消耗,就引入了轻量级的进程----线程。

        线程的特点:同一个进程创建的多个线程共用同一个进程的地址空间。当进程创建线程后,原本的进程也叫做线程,成为主线程。

        那么线程又是如何创建的?我们先来看一下创建线程的函数:

#include 

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

乍一看,这个函数如此复杂,竟然有四个参数。没关系,我们一个一个的来看。

① thread 这个参数代表着线程对象,每个线程对应一个线程对象。

在使用之前得先定义出来,看它类型,可以看出是pthread_t 类型的。

② attr 这是线程属性,默认填缺省属性NULL。

③ void *(*start_routine)(void *)   这个东西看起来复杂,其实就是填函数名,线程用一个函数去封装,所以填的这个函数名就是我们线程的入口。

④  arg 这个是填需要往线程里传递的参数。一次只能传递一个参数,想要传递多个参数的话,可以先定义一个结构体,传递结构体名,不想传递参数就填NULL。

        从函数中可以看出返回值是int 类型的,创建线程成功返回0,失败则返回1,可用于判断线程是否创建成功。

         和进程一样,有创建自然就有结束线程和等待线程。先来看结束线程的函数:

#include 

void pthread_exit(void *reatval);

        就reatval 一个参数,这个参数填的是线程结束时返回的信息,由等待线程函数接收,若不想返回信息,可填NULL。

        等待线程:就是主线程需要进行的操作,因为线程是共用主线程的同一片地址空间,所以假如主线程先结束,那么子线程也随即结束。所以要想主线程在子线程后面结束就必须有这个等待函数 函数如下:

#include 

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

参数:① thread 需要等待的线程对象

            ②retval 可以看出它是一个二级指针,二级指针需要传递一级指针,而且是void 类型的。用于接收结束线程函数返回的内容,可为NULL。

函数都知道了,我们来开始创建线程:

#include 
#include 
#include 
#include 
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  %cn",t->a,t->b); 打印查看通过传递结构体传递的参数
            sleep(1);  //每隔一秒执行一次 便于观察
        }
}

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

        接下来,我们要实现线程间通信,因为是共用一片地址空间,所以线程间通信可以通过全局变量来实现。因为通过全局变量来实,所以在一个线程使用全局变量的同时,其他线程也在访问该数据 那么一些线程就会遭到破坏。所以我们就可以通过线程的同步和线程的互斥来解决这个问题。

        什么是同步和互斥,如何让线程间同步或线程间互斥?

同步:多个线程之间,按照约定的顺序先后来执行程序。

同步中我们有一个东西叫做信号量,各个线程想要执行这个对应的程序就必须要有信号量,若信号量为0;则进入阻塞状态。操作信号量必须通过特定的函数接口才能访问,不可用运算符去直接操作。信号量本质时一个非负整数。

初始化信号量:

#include 

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

参数:

        ① sem 信号量,类似线程对象,需要定义set_t 类型。

        ② pshared 用于线程间同步 须填0。

        ③ value  信号量初始值。

 线程运行时信号量时随需要改变的,所以信号量申请有释放。

信号量申请(P操作):

#include 

int sem_wait(set_t *sem);

        调用一次申请一个数据量。

信号量释放(V操作):

#include 

int sem_post(set_t *sem);

         调用一次释放一个数据量

接下来看看如何让线程同步:

#include 
#include 
#include 
#include 
#include 
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。
    }
}

如此,我们就可以实现线程间通信了。

互斥:当一个线程使用共用数据时 其他线程都不能使用该数据。

        多个线程能够共同访问的数据叫做临界资源。涉及到临界资源的代码块叫做临界区,互斥便是用互斥锁保护临界区。

让线程同步同样有三个函数:

①  互斥锁的初始化

#include 

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

参数:

         mutex--互斥锁,同信号量对象,线程对象一样定义。

          attr--填NULL。

返回值:

          成功返回0,失败返回-1。

②  申请锁(上锁):

#include 

int pthread_mutex_lock(pathread_mutex_t *mutex);

③  释放锁(解锁):

#include 

int pthread_mutex_unlock(pathread_mutex_t *mutex);

接下来看看如何让线程互斥:

#include 
#include 
#include 
#include 
#include 
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=%dn",num1,num2);
        pthread_mutex_unlock(&mutex); //解锁
        }
    }
}

好了 各位读者姥爷,今天的分享到此结束了,哪里写的不对的地方欢迎多多指正,我们明天继续学习,感谢观看。

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1039001.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号