linux下同步与互斥

目录

1、互斥锁

1.1 普通互斥锁

1.2 递归锁

2、读写锁

3、自旋锁

4、条件变量

4.1 条件变量的原理和接口

4.2 虚假唤醒

4.3 broadcast的例子

5、信号量

5.1 信号量用于同步

5.2 信号量用于互斥

5.3 sem_timedwait的例子

6、pthread标准的其它函数

6.1 pthread_self (void) ;

6.2 pthread_exit (void * retval) ;

6.3 pthread_cance (pthread_t thread) ;

7、Windows与Linux接口对比

最后:linux带pthread编译:


锁主要有两个用途:

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] &
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值