文章目录
- 一、Linux上线程开发互斥锁概要
- 二、创建及销毁互斥锁
- 2.1 示例:主线程等待两个线程退出,1线程和2线程打印信息
- 三、互斥量的初始化问题
一、Linux上线程开发互斥锁概要
互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后,释放互斥量上的锁。 对互斥量进行加锁后,任何其它试图在再次对互斥量加锁的进行会被阻塞直到当前线程释放该互斥量上的锁。如果释放互斥锁时,有多个线程阻塞,则所有在该互斥锁的线程都会变成可运行状态,第一个变成可运行状态的线程可以对互斥量加锁,其它线程将会看到互斥锁依然被锁住,只能等待它从新变成可用。在这种方式下,每次只有一个线程可以向前运行。
在设计时需要规定所有的线程必须遵守相同的数据访问原则。只有这样,互斥机制才能正常工作。操作系统并不会做数据访问的串行化。如果允许其中的某个线程在没有得到锁的情况下也可以访问共享资源,那么即使其它的线程在使用共享资源前都获取了锁,也还是会出现数据不一致的问题。
互斥量变量使用 pthread_mutex_t
的数据类型表示。在使用互斥变量前必须对它进行初始化,可以把它置为常量 PTHREAD_MUTEX_INITALIZER
(只对静态分配的互斥量),也可以通过调用 pthread_mutex_init
函数进行初始化。如果动态地分配互斥量(比如通过调用malloc函数),那么在释放内存前需要调用 pthread_mutex_destroy
。
二、创建及销毁互斥锁
对锁的操作
#include <pthread.h>
// 动态初始化
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
// 销毁锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
// 返回:若成功返回0,否则返回错误编号
2.1 示例:主线程等待两个线程退出,1线程和2线程打印信息
不加锁的程序:
//demo5
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int g_data=0;
void* fun1(void *arg)
{
printf("t1:%ld thread is create\n", (unsigned long)pthread_self());
printf("t1: %d\n", *((int*)arg));
}
void* fun2(void *arg)
{
printf("t2:%ld thread is create\n", (unsigned long)pthread_self());
printf("t2: %d\n", *((int*)arg));
}
int main()
{
//int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
int ret;
int param = 100;
pthread_t t1;
pthread_t t2;
// 线程属性 线程函数 线程函数参数
ret = pthread_create(&t1, NULL, fun1, (void*)¶m);
if(ret == 0){
printf("main:create t1 success\n");
} else {
perror("why t1 fail");
}
ret = pthread_create(&t2, NULL, fun2, (void*)¶m);
if(ret == 0){
printf("main:create t1 success\n");
} else {
perror("why t2 fail");
}
printf("main:%ld\n", (unsigned long)pthread_self());//打印主线程ID
pthread_join(t1, NULL);//等待线程结束,防止进程结束,线程还未执行完毕
pthread_join(t2, NULL);//等待线程结束,防止进程结束,线程还未执行完毕
return 0;
}
从运行结果看出:执行顺序不一样,每次都不是固定的顺序。
思考:那我们能不能让 t1先运行,t2运行,然后 main 最后运行?能不能保证?能!得加钱!啊不,得加锁
想让t1先运行,需要先定义一个锁。被锁锁住的代码都叫做互斥量。
加锁步骤:
创建一个锁,锁的名字叫做mutex,是一个全局变量,线程1和线程2都能看到这把锁。
在创建线程之前对锁进行初始化
程序退出后,要销毁这把锁
想让t1先运行,对t1进行上锁:先上锁,打印两句话,然后解锁。
同理,t2也是
运行结果:
编译 -> 运行 -> 结果:都是t1先执行(由于没加延时,可能存在偶然性,每次恰好是t1先执行完例程,因为计算机算力太强了,程序很小)
修改t1:让多执行几次(几百几千次也行,可以大一点,或者选择加延时函数),测试互斥锁。
运行结果:
互斥量就是一把锁,被锁锁住的代码都叫做互斥量。对锁操作有加锁操作 pthread_mutex_lock()
和解锁操作 pthread_mutex_unlock
。在加锁和解锁之间的代码,称之为共享资源。
可见,对于共享资源的使用,只有锁被释放后,其它依赖共享资源的线程才能得以继续执行。
三、互斥量的初始化问题
可以使用宏进行初始化,属于是静态初始化。
在使用互斥变量前必须进行初始化,可以置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量);也可以通过调用pthread_mutex_init、pthread_cond_init函数进行初始化。如果动态地分配互斥量,那么在释放内存前需要调用pthread_mutex_destroy。
动态初始化:
pthread_mutex_t mutex;
主函数中必须:
pthread_mutex_init(&mutex, NULL); //dynamic init
使用宏进行初始化(静态初始化):
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // static init