linux多线程(线程,互斥锁,条件变量)

线程

1.1        使用线程的理由

         在面向线程设计的系统中,进程不是基本运行单位,而是线程的容器。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程可执行不同的任务。

        进程有独立的地址空间,一个进程崩溃后,不会对其他进程产生影响。而线程只是一个进程中的不同执行路径,线程没有单独的地址空间,它们共享大部分数据,一个线程死掉就相当于整个进程死掉。

        所以:多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行又要共享某些变量的并发操作,一般使用线程(进程间通信也能实现,但相对较麻烦)。

1.2     Linux上线程相关的API   

 功能:创建一个新的线程(pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。)
 原型
 		int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
 参数
 		thread:返回线程ID 
 		attr:设置线程的属性,attr为NULL表示使用默认属性 
 		start_routine:是个函数地址,线程启动后要执行的函数 
 		arg:传给线程启动函数的参数

 新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg。如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。


功能:线程终止
原型
		void pthread_exit(void *value_ptr);
参数
	  value_ptr:value_ptr不要指向一个局部变量。
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身

 参数:rval_ptr是一个无类型指针(注意不要指向一个局部变量),与传给启动例程的单个参数类似。进程中的其他线程可以通过调用pthread_join函数访问到这个指针。

功能:等待线程结束
原型
	 int pthread_join(pthread_t thread, void **value_ptr);
参数
	 thread:要等待的线程ID
	 value_ptr:它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码

调用这个函数的线程将会一直阻塞,直到指定的线程调用pthread_exit

pthread_exit()函数将终止调用线程,且其返回值可由另一线程通过调用 pthread_join()来获取

功能:获取线程ID
 原型
 		pthread_t pthread_self(void);
 返回值
        调用线程的ID

1.3        代码案例

#include <stdio.h>
#include <pthread.h>
void *func1(void *arg)
{
    static int ret = 10;
    printf("t1: %ld thread is create\n", (unsigned long)pthread_self());
    printf("t1: param is %d\n", *((int *)arg));
    pthread_exit((void *)&ret);//退出线程
}
int main()
{
    int ret;
    pthread_t t1;
    int *pret = NULL;
    int param = 100;
    ret = pthread_create(&t1, NULL, func1, (void *)&param);//创建线程
    if (ret == 0)
    {
        printf("main: create t1 success\n");
    }
    printf("main:%ld\n", (unsigned long)pthread_self());
    pthread_join(t1, (void **)&pret);//等待线程
    printf("main: ti quit: %d\n", *pret);
    return 0;
}

配合函数原型看 搞清楚以上代码为什么要使用强转符

打印结果如下

 通过以上代码就创建了新的线程t1,同时打印了main线程和t1线程对应的线程ID,并且通过pthread_joinpthread_exit的使用 获取到了t1线程的退出码10。


互斥锁

首先上一段代码

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int g_data = 0;
void *func1(void *arg)
{
    printf("t1: %ld thread is create\n", (unsigned long)pthread_self());
    printf("t1: param is %d\n", *((int *)arg));
    while (1)
    {
        printf("t1: %d\n", g_data++);
        sleep(1);
        if (g_data == 3)
            pthread_exit(NULL);
    }
}
void *func2(void *arg)
{
    printf("t2: %ld thread is create\n", (unsigned long)pthread_self());
    printf("t2: param is %d\n", *((int *)arg));
    while (1)
    {
        printf("t2: %d\n", g_data++);
        sleep(1);
    }
}
int main()
{
    int ret;
    pthread_t t1;
    pthread_t t2;
    int param = 100;
    ret = pthread_create(&t1, NULL, func1, (void *)&param);
    if (ret == 0)
    {
        printf("main: create t1 success\n");
    }
    ret = pthread_create(&t2, NULL, func2, (void *)&param);
    if (ret == 0)
    {
        printf("main: create t2 success\n");
    }
    printf("main:%ld\n", (unsigned long)pthread_self());
    while (1)
    {
        printf("main: %d\n", g_data++);
        sleep(1);
    }
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    return 0;
}

打印结果

 

 

    我们设置了一个全局变量g_data,目的想在g_data加到3的时候,退出t1线程。而实际情况是3可能会被t2线程或main线程  “抢走”  ,t1仍然不停的在打印,所以可能实现不了预想功能。

于是我们想让g_data加到3的这个过程发生在t1线程中,这样就能实现该功能了。于是引入互斥锁。

2.1        互斥锁概念

在线程实际运行过程中,我们经常需要多个线程保持同步。这时可以用互斥锁来完成任务。

作用:防止多线程对同一个数据进行操作 。

2.2        与互斥锁相关的API

 功能:创建及销毁互斥锁
 原型
 		int pthread_mutex_init(pthread_mutex_t *restrict mutex, 
                       const pthread_mutexattr_t *restrict attr);
        int pthread_mutex_destroy(pthread_mutex_t *mutex);
 参数
 		mutex 所需创建的锁
        attr 创建锁属性,一般默认为NULL
返回值
        成功后返回0,其他任何返回值都表示出现错误。

以上为互斥锁的动态创建
也可以用宏PTHREAD_MUTEX_INITIALIZER来静态的初始化锁

互斥锁是pthread_mutex_t的结构体,而这个宏是一个结构常量,如下可以完成静态的初始化锁:

pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;
 功能: 加锁及解锁
 原型
 		int pthread_mutex_lock(pthread_mutex_t *mutex);
        int pthread_mutex_trylock(pthread_mutex_t *mutex);
        int pthread_mutex_unlock(pthread_mutex_t *mutex);
 参数
 		mutex 所需操作的锁

使用互斥锁改进以上代码

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int g_data = 0;
pthread_mutex_t mutex;
void *func1(void *arg)
{
    printf("t1: %ld thread is create\n", (unsigned long)pthread_self());
    printf("t1: param is %d\n", *((int *)arg));
    pthread_mutex_lock(&mutex); //上锁
    while (1)
    {
        printf("t1: %d\n", g_data++);
        sleep(1);
        if (g_data == 3)
        {
            pthread_mutex_unlock(&mutex); //解锁
            printf("t1 quit========================\n");
            pthread_exit(NULL); //退出线程
            //exit(0);  会直接退出进程
        }
    }
}
void *func2(void *arg)
{
    printf("t2: %ld thread is create\n", (unsigned long)pthread_self());
    printf("t2: param is %d\n", *((int *)arg));
    while (1)
    {
        printf("t2: %d\n", g_data);
        pthread_mutex_lock(&mutex); //上锁
        g_data++;
        pthread_mutex_unlock(&mutex); //解锁
        sleep(1);
    }
}
int main()
{
    int ret;
    pthread_t t1;
    pthread_t t2;
    int param = 100;
    pthread_mutex_init(&mutex, NULL); //创建一把锁
    ret = pthread_create(&t1, NULL, func1, (void *)&param);
    if (ret == 0)
    {
        printf("main: create t1 success\n");
    }
    ret = pthread_create(&t2, NULL, func2, (void *)&param);
    if (ret == 0)
    {
        printf("main: create t2 success\n");
    }
    printf("main:%ld\n", (unsigned long)pthread_self());
    while (1)
    {
        printf("main: %d\n", g_data);
        sleep(1);
    }
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_mutex_destroy(&mutex); //销毁锁
    return 0;
}

打印结果

 

以上代码中若1线程先运行 1 2 3 都是在1线程中加的

2线程先运行 1是在2线程中加的 2 3 是在1线程中加的所以1线程一定能拿到3 正常退出线程

1线程首先上锁后,会一直运行到1线程解锁,其余线程才会运行


条件变量

3.1        条件变量的概念

        条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。可以让线程休眠等待某个条件,条件满足唤醒线程去检测这个条件是否真的满足的机制。

3.2        与条件变量相关的API

 功能: 创建及销毁条件变量
 原型
 		int pthread_cond_init(pthread_cond_t *restrict cond,
                      const pthread_condattr_t *restrict attr);
        int pthread_cond_destroy(pthread_cond_t *cond);
 参数
 		cond 操作的条件变量
        attr 一般为NULL
 功能: 等待
 原型
        int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
        int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, cond struct timespec *restrict timeout);
 参数
 		cond 操作的条件变量
        mutex 所需操作的锁
 功能: 触发
 原型
        int pthread_cond_signal(pthread_cond_t *cond);
        int pthread_cond_broadcast(pthread_cond_t *cond);
 参数
 		cond 操作的条件变量

代码案例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int g_data = 0;
pthread_mutex_t mutex;
pthread_cond_t cond;
void *func1(void *arg)
{
    printf("t1: %ld thread is create\n", (unsigned long)pthread_self());
    printf("t1: param is %d\n", *((int *)arg));
    while (1)
    {
        pthread_cond_wait(&cond, &mutex); //等待 处于睡眠状态 除非2线程唤醒它
        printf("t1 run========================\n");
        printf("t1: %d\n", g_data);
        g_data = 0;
        sleep(1);
    }
}
void *func2(void *arg)
{
    printf("t2: %ld thread is create\n", (unsigned long)pthread_self());
    printf("t2: param is %d\n", *((int *)arg));
    while (1)
    {
        printf("t2: %d\n", g_data);
        pthread_mutex_lock(&mutex); //上锁
        g_data++;
        if (g_data == 3)
        {
            pthread_cond_signal(&cond); //触发  g_data加到3时唤醒1线程内的等待
        }
        pthread_mutex_unlock(&mutex); //解锁
        sleep(1);
    }
}
int main()
{
    int ret;
    pthread_t t1;
    pthread_t t2;
    int param = 100;
    pthread_mutex_init(&mutex, NULL); //创建一把锁
    pthread_cond_init(&cond, NULL);   //创建一个条件

    ret = pthread_create(&t1, NULL, func1, (void *)&param);

    ret = pthread_create(&t2, NULL, func2, (void *)&param);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_mutex_destroy(&mutex); //销毁锁
    pthread_cond_destroy(&cond);   //销毁条件
    return 0;
}

打印结果

 以上代码实现了在线程t1中每次将g_data加到3后运行线程t1,同时清零g_data

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值