目录
6.2 pthread_exit (void * retval) ;
6.3 pthread_cance (pthread_t thread) ;
锁主要有两个用途:
1 解决互斥访问资源
2 解决一个任务对另一个任务的依赖
关于linux内核:
linux内核只提供了轻量进程的支持,未实现线程模型。Linux是一种“多进程单线程”的操作系统。Linux本身只有进程的概念,而其所谓的“线程”本质上在内核里仍然是进程。
1、互斥锁
1.1 普通互斥锁
这里普通互斥锁指的是非递归互斥锁。
为了保护共享资源,使我们线程可以单独使用某个共享资源,使用之前先上锁,当其他进程要使用的时候,就需要等待到这个线程用完之后,再开锁。
接口与定义:
声明互斥锁:
pthread_mutex_t m;
初始化互斥锁:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
第一个参数:就是我们前面声明的锁,因为这个参数需要传递的是一个锁的指针,所以需要有一个取地址符。
第二个参数:是这个锁的属性,我们让它是默认的属性,这里设置为NULL
返回值:成功返回0, 失败返回-1
上锁:锁住某个资源
int pthread_mutex_lock(pthread_mutex_t *mutex); 这个函数是阻塞型。
int pthread_mutex_trylock(pthread_mutex_t *mutex); 这个是非阻塞型的。
返回值:成功返回0,失败返回-1
解锁:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回-1
销毁互斥锁:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
返回值:成功返回0, 失败返回-1
实例 启动两个线程对同一个全局数组写入:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
pthread_mutex_t mutex; //定义互斥量
char* buf[5]; //字符指针数组 全局变量
int pos; //用于指定上面数组的下标
void *task(void *p)
{
//使用互斥量进行加锁
pthread_mutex_lock(&mutex);
buf[pos] = (char *)p;
sleep(1);
pos++;
//使用互斥量进行解锁
pthread_mutex_unlock(&mutex);
}
void *task2(void *p)
{
//使用互斥量进行加锁
pthread_mutex_lock(&mutex);
buf[pos] = (char *)p;
sleep(1);
pos++;
//使用互斥量进行解锁
pthread_mutex_unlock(&mutex);
}
int main(void)
{
//初始化互斥量, 默认属性
pthread_mutex_init(&mutex, NULL);
//启动一个线程 向数组中存储内容
pthread_t tid, tid2;
pthread_create(&tid, NULL, task, (void *)"zhangfei");
pthread_create(&tid2, NULL, task2, (void *)"guanyu");
//主线程进程等待,并且打印最终的结果
pthread_join(tid, NULL);
pthread_join(tid2, NULL);
pthread_mutex_destroy(&mutex);
int i = 0;
printf("字符指针数组中的内容是:");
for (i = 0; i < pos; ++i)
{
printf("%s ", buf[i]);
}
输出:zhangfei guanyu
1.2 递归锁
递归锁也叫可重入锁(reentrant mutex),非递归锁也叫不可重入锁(non-reentrant mutex)。
同一个线程可以多次获取同一个递归锁,不会产生死锁。如果一个线程多次获取同一个非递归锁,则会产生死锁。
Windows下的Mutex和Critical Section是可递归的。
Linux下的pthread_mutex_t锁是默认是非递归的。可以通过设置PTHREAD_MUTEX_RECURSIVE属性,将pthread_mutex_t锁设置为递归锁。
//create recursive attribute
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
//set recursive attribute
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&g_mutex, &attr);
可能使用到的场景:
//这两个函数不能同时运行,因为它们会修改类的内部,
//因此我想使用互斥锁。问题是,Process()在内部调用reset(),
//因为已经获取了mMutex,它将创建死锁。使用递归锁锁定它们可以解决此问题。
public void Process(...)
{
acquire_mutex(mMutex);
// Heavy processing
...
reset();
...
release_mutex(mMutex);
}
public void reset()
{
acquire_mutex(mMutex);
// Reset
...
release_mutex(mMutex);
}
模拟实现:
class RecursiveMutex : protected Mutex
{
private:
volatile int32 _currentThread;
int32 _lockCount;
...
}
void RecursiveMutex::Take() throw(Exception)
{
if ( !IsThisThreadOwner() )
{
Mutex::Take();//只有当不是当前线程的时候才会执行pthread_mutex_lock
MakeThisThreadOwner();
}
AtomicIncrement( _lockCount );
}
IsThisThreadOwner: 如果这个mutex的_currentThread是当前线程则返回true
不管是不是,都会把_currentThread变成当前线程
如果就是当前线程正在占有的该mutex,则只是计数+1
2、读写锁
读写锁也是用来控制互斥访问,特点是:
- 如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作;
- 如果有其它线程写数据,则其它线程都不允许读、写操作。
所以读写锁的规则就是:
- 如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁;
- 如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁。
读写锁适合于对资源的读次数比写次数多得多的情况。
读写锁的接口:
#include <pthread.h>
// 初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *rwlock,
const pthread_rwlockattr_t *attr);
// 申请读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock );
// 申请写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock );
// 非阻塞的方式获取写锁
// 如果有任何的读者或写者持有该锁,则立即失败返回。
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
// 解锁
int pthread_rwlock_unlock (pthread_rwlock_t *rwlock);
// 销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
实例,两个读线程两个写线程,写互斥地对字符数组操作:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
char cBuf[30];
pthread_rwlock_t rwlock; //读写锁
//write线程函数,负责往buf里面加字符
void *taskWrite(void *p)
{
while (1)
{
pthread_rwlock_wrlock(&rwlock);
if (pos < 29)
{
cBuf[pos] = 'A';
pos++;
printf("Write cBuf is: %s \n", cBuf);
}
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
}
//write线程函数,负责从buf里面删除字符
void *taskWriteDel(void *p)
{
while (1)
{
pthread_rwlock_wrlock(&rwlock);
if (pos > 0)
{
cBuf[pos-1] &