linux - 线程同步

线程同步

概念

线程同步,指一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其它线程为保证数据一致性,不能调用该功能。

demo:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/types.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#define NUM 20000
int number = 0;

void *mythread1(void *arg)
{
        int i = 0;
        int n;
        for(;i<NUM;i++)
        {
                n = number;
                n++;
                number = n;
                printf("thread1 number = %d\n",number);
        }
}


void *mythread2(void *arg)
{   
    int i = 0;
    int n;
    for(;i<NUM;i++)
    {
        n = number;
        n++;
        number = n;
        printf("thread2 number = %d\n",number);
    }
}

int main(int argc,char* args[])
{
        //int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
    //                      void *(*start_routine) (void *), void *arg);
        //创建线程
    pthread_t thread1;
    int ret = pthread_create(&thread1,NULL,mythread1,NULL);
    if(ret!=0)
    {
            printf("pthread_create error %s\n",strerror(errno));
            return -1;
    }

    pthread_t thread2;
    ret = pthread_create(&thread2,NULL,mythread2,NULL);
    if(ret!=0)
    {
        printf("pthread_create error %s\n",strerror(errno));
        return -1;
    }
        pthread_join(thread1,NULL);
        pthread_join(thread2,NULL);
        printf("number = %d\n",number);
        return 0;
}

互斥锁

Linux中提供一把互斥锁mutex(也称之为互斥量)。每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。
资源还是共享的,线程间也还是竞争的,但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。
在这里插入图片描述

线程1访问共享资源的时候要先判断锁是否锁着,如果锁着就阻塞等待;若锁是解开的就将这把锁加锁,此时可以访问共享资源,访问完成后释放锁,这样其他线程就有机会获得锁。

应该注意:图中同一时刻,只能有一个线程持有该锁,只要该线程未完成操作就不释放锁。
使用互斥锁之后,两个线程由并行操作变成了串行操作,效率降低了,但是数据不一致的问题得到解决了。

pthread_mutex_t

在LinuxThreads实现中,pthread_mutex_t是一个结构;
pthread_mutex_t mutex; 变量mutex只有两种取值1、0。

pthread_mutex_init

初始化一个互斥锁(互斥量) ;

int pthread_mutex_init(pthread_mutex_t *restrict mutex, 
						    const pthread_mutexattr_t *restrict attr);
  • mutex:传出参数,调用时应传 &mutex
  • attr:互斥锁属性。是一个传入参数,通常传NULL,选用默认属性(线程间共享)。
    restrict关键字:只用于限制指针,告诉编译器,所有修改该指针指向内存中内容的操作,只能通过本指针完成。不能通过除本指针以外的其他变量或指针修改互斥量mutex的两种初始化方式:
  • 静态初始化:如果互斥锁 mutex 是静态分配的(定义在全局,或加了static关键字修饰),可以直接使用宏进行初始化。
    pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER;
    动态初始化:局部变量应采用动态初始化。
    pthread_mutex_init(&mutex, NULL)

pthread_mutex_destroy

销毁一个线程锁;

int pthread_mutex_destroy(pthread_mutex_t *mutex);

mutex:互斥锁变量;

pthread_mutex_lock

对互斥所加锁;

int pthread_mutex_lock(pthread_mutex_t *mutex);

mutex:互斥锁变量;

pthread_mutex_unlock

对互斥所解锁;

int pthread_mutex_unlock(pthread_mutex_t *mutex);

pthread_mutex_trylock

尝试加锁

int pthread_mutex_trylock(pthread_mutex_t *mutex);

demo:

//对上面的代码进行修改,加锁
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/types.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#define NUM 20000
int number = 0;
pthread_mutex_t mutex;      //定义互斥锁
void *mythread1(void *arg)
{
    int i = 0;
    int n;
    for(;i<NUM;i++)
    {
        pthread_mutex_lock(&mutex);			//加锁
        n = number;
        n++;
        number = n;
        printf("thread1 number = %d\n",number);
        pthread_mutex_unlock(&mutex);		//解锁
    }
}


void *mythread2(void *arg)
{
    int i = 0;
    int n;
    for(;i<NUM;i++)
    {
        pthread_mutex_lock(&mutex);
        n = number;
        n++;
        number = n;
        printf("thread2 number = %d\n",number);
        pthread_mutex_unlock(&mutex);
    }
}

int main(int argc,char* args[])
{
    //int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
    //                      void *(*start_routine) (void *), void *arg);
    //创建线程
    pthread_mutex_init(&mutex,NULL);        //默认为互斥锁
    pthread_t thread1;
    int ret = pthread_create(&thread1,NULL,mythread1,NULL);
    if(ret!=0)
    {
        printf("pthread_create error %s\n",strerror(errno));
        return -1;
    }

    pthread_t thread2;
    ret = pthread_create(&thread2,NULL,mythread2,NULL);
    if(ret!=0)
    {
        printf("pthread_create error %s\n",strerror(errno));
        return -1;
    }
    pthread_join(thread1,NULL);
    pthread_join(thread2,NULL);
    pthread_mutex_destroy(&mutex);
    printf("number = %d\n",number);
    return 0;
}

死锁

操作系统并不会给我们提供死锁这种机制,出现死锁只能是用户使用互斥锁引起的不良现象;

常见死锁有两种:
第一种:自己锁自己:

void *mythread1(void *arg)
{
    int i = 0;
    int n;
    for(;i<NUM;i++)
    {
        pthread_mutex_lock(&mutex);
        pthread_mutex_lock(&mutex);		//重复加锁
        n = number;
        n++;
        number = n;
        printf("thread1 number = %d\n",number);
        pthread_mutex_unlock(&mutex);
    }
}

第二种:线程A拥有A锁,请求获得B锁;线程B拥有B锁,请求获得A锁,这样造成线程A和线程B都不释放自己的锁,而且还想得到对方的锁,从而产生死锁,如下图所示:
在这里插入图片描述

死锁解决方案:

  • 让线程按照一定的顺序去访问共享资源
  • 在访问其他锁的时候,需要先将自己的锁解开
  • 利用pthread_mutex_trylock判断

读写锁

读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。

读写锁特性:

  • 读写锁是“写模式加锁”时,解锁前,所有对该锁加锁的线程都会被阻塞。
  • 读写锁是“读模式加锁”时,如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。
  • 读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高

pthread_rwlock_t

定义一个读写锁;

pthread_rwlock_init(pthread_rwlock_t * ,pthread_rwattr_t *);

返回值:0,表示成功,非0为一错误码

pthread_rwlock_init

初始化读写锁;

int pthread_rwlock_init(pthread_rwlock_t *rwlock, 

        const pthread_rwlockattr_t *attr);

函数参数:

  • rwlock读写锁
  • attr读写锁属性,传NULL为默认

pthread_rwlock_destroy

销毁读写锁;

pthread_rwlock_destroy(pthread_rwlock_t* );

pthread_rwlock_rdlock

阻塞式读锁;

pthread_rwlock_rdlock(pthread_rwlock_t*);

pthread_rwlock_tryrdlock

非阻塞式读锁;

pthread_rwlock_tryrdlock(pthread_rwlock_t*);

pthread_rwlock_wrlock

阻塞式写锁;

pthread_rwlock_wrlock(pthread_rwlock_t*);

pthread_rwlock_trywrlock

非阻塞式写锁;

pthread_rwlock_trywrlock(pthread_rwlock_t*);

pthread_rwlock_unlock

解锁;

pthread_rwlock_unlock(pthread_rwlock_t*);

demo:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/times.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
int number = 0;
pthread_rwlock_t rwlock;        //定义读写锁
//写线程回调函数
void *thread_write(void *arg)
{
    int i = *(int *)arg;
    int cur;
    while(1)
    {
        //加锁
        pthread_rwlock_wrlock(&rwlock);
        cur = number;
        cur++;
        number = cur;
        printf("%d -w = %d\n",i,cur);
        pthread_rwlock_unlock(&rwlock);
        sleep(rand()%3);
    }
}

//读线程回调函数
void *thread_read(void *arg)
{
    int i = *(int *)arg;
    int cur;
    while(1)
    {
        pthread_rwlock_rdlock(&rwlock);
        cur = number;
        printf("%d - r = %d\n",i,cur);
        pthread_rwlock_unlock(&rwlock);
        sleep(rand()%3);
    }
}
int main(int argc,char* args[])
{
    int n = 8;
    int i = 0;
    int arr[8];
    pthread_t thread[8];

    pthread_rwlock_init(&rwlock,NULL);
    for(;i < 3; i++)        //创建子线程
    {
        arr[i] = i;
        pthread_create(&thread[i], NULL, thread_write, &arr[i]);
    }
    //创建五个读线程
    for(;i < 5; i++)        //创建子线程
    {
        arr[i] = i;
        pthread_create(&thread[i], NULL, thread_read, &arr[i]);
    }
    //回收子线程
    int j = 0;
    for(; j < n; j++)
    {
        pthread_join(thread[j],NULL);
    }
    pthread_rwlock_unlock(&rwlock);
    return 0;
}

条件变量

条件本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。

pthread_cond_t

定义一个条件变量

pthread_cond_init

初始化条件变量

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

pthread_cond_distroy

销毁条件变量

pthread_cond_wait

等待条件变量

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);

pthread_cond_signal()

发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行;

int pthread_cond_signal(pthread_cond_t *cond);

pthread_cond_broadcast

激发所有的等待线程,使其全部开始执行;

int pthread_cond_broadcast(pthread_cond_t *cond);

demo:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct node
{
    int data;
    struct node *next;
}NODE;

NODE *head = NULL;

//定义一把锁
pthread_mutex_t mutex;

//定义条件变量
pthread_cond_t cond;

//生产者线程
void *producer(void *arg)
{
    NODE *pNode = NULL;
    while(1)
    {
        //生产一个节点
        pNode = (NODE *)malloc(sizeof(NODE));
        if(pNode==NULL)
        {
            perror("malloc error");
            exit(-1);
        }
        pNode->data = rand()%1000;
        printf("P:[%d]\n", pNode->data);

        //加锁
        pthread_mutex_lock(&mutex);

        pNode->next = head;
        head = pNode;

        //解锁
        pthread_mutex_unlock(&mutex);

        //通知消费者线程解除阻塞
        pthread_cond_signal(&cond);

        sleep(rand()%3);
    }
}


//消费者线程
void *consumer(void *arg)
{
    NODE *pNode = NULL;
    while(1)
    {
        //加锁
        pthread_mutex_lock(&mutex);

        if(head==NULL)
        {
            //若条件不满足,需要阻塞等待
            //若条件不满足,则阻塞等待并解锁;
            //若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁 
            pthread_cond_wait(&cond, &mutex);
        }

        printf("C:[%d]\n", head->data);
        pNode = head;
        head = head->next;

        //解锁
        pthread_mutex_unlock(&mutex);

        free(pNode);
        pNode = NULL;

        sleep(rand()%3);
    }
}

int main()
{
    int ret;
    pthread_t thread1;
    pthread_t thread2;

    //初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    //条件变量初始化
    pthread_cond_init(&cond, NULL);

    //创建生产者线程
    ret = pthread_create(&thread1, NULL, producer, NULL);
    if(ret!=0)
    {
        printf("pthread_create error, [%s]\n", strerror(ret));
        return -1;
    }

    //创建消费者线程
    ret = pthread_create(&thread2, NULL, consumer, NULL);
    if(ret!=0)
    {
        printf("pthread_create error, [%s]\n", strerror(ret));
        return -1;
    }

    //等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    //释放互斥锁
    pthread_mutex_destroy(&mutex);

    //释放条件变量
    pthread_cond_destroy(&cond);

    return 0;
}

信号量

信号量相当于多把锁, 可以理解为是加强版的互斥锁;

sem_t

信号量变量

sem_init

初始化信号量;

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

sem: 信号量变量
pshared: 0表示线程同步, 1表示进程同步
value: 最多有几个线程操作共享数据

sem_wait

调用该函数一次, 相当于sem–, 当sem为0的时候, 引起阻塞

int sem_wait(sem_t *sem);

sem_post

调用一次, 相当于sem++
调用信号量

int sem_post(sem_t *sem);

sem_trywait

尝试加锁, 若失败直接返回, 不阻塞

sem_destroy

销毁信号量

demo:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
typedef struct node
{
	int data;
	struct node *next;
}NODE;

NODE *head = NULL;

//定义信号量
sem_t sem_producer;
sem_t sem_consumer;

//生产者线程
void *producer(void *arg)
{
	NODE *pNode = NULL;
	while(1)
	{
		//生产一个节点
		pNode = (NODE *)malloc(sizeof(NODE));
		if(pNode==NULL)
		{
			perror("malloc error");
			exit(-1);
		}
		pNode->data = rand()%1000;
		printf("P:[%d]\n", pNode->data);

		//加锁
		sem_wait(&sem_producer); //--

		pNode->next = head;
		head = pNode;

		//解锁
		sem_post(&sem_consumer);  //相当于++

		sleep(rand()%3);
	}
}


//消费者线程
void *consumer(void *arg)
{
	NODE *pNode = NULL;
	while(1)
	{
		//加锁
		sem_wait(&sem_consumer); //相当于--
		
		printf("C:[%d]\n", head->data);	
		pNode = head;
		head = head->next;

		//解锁
		sem_post(&sem_producer); //相当于++

		free(pNode);
		pNode = NULL;

		sleep(rand()%3);
	}
}

int main()
{
	int ret;
	pthread_t thread1;
	pthread_t thread2;

	//初始化信号量
	sem_init(&sem_producer, 0, 5);
	sem_init(&sem_consumer, 0, 0);

	//创建生产者线程
	ret = pthread_create(&thread1, NULL, producer, NULL);
	if(ret!=0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	//创建消费者线程
	ret = pthread_create(&thread2, NULL, consumer, NULL);
	if(ret!=0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	//等待线程结束
	pthread_join(thread1, NULL);
	pthread_join(thread2, NULL);

	//释放信号量资源
	sem_destroy(&sem_producer);
	sem_destroy(&sem_consumer);

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值