• c语言并行程序设计之路(三)(信号量与同步)


    0.前言

    c语言并行程序设计之路(二)(忙等待与锁)中提到,忙等待总是浪费CPU的资源,但它能事先确定线程执行临界区代码的顺序;如果采用互斥量,那么那个线程先进入临界区以及此后的顺序由系统随机选取。

    因为加法计算是可交换的,所以(pi)计算程序的结果不受线程执行顺序的影响。但是,仍然有情况需要控制线程进入临界区的顺序,比如矩阵乘法。

    1.问题描述

    假设有t个线程,线程0向线程1发送消息,线程1向线程2发送消息,...,线程t-2向线程t-1发送消息,线程t-1向线程0发送消息。当一个线程“接收”一条消息后,它打印消息并终止。为了实现消息的传递,分配了一个char*类型的共享数组,每个线程初始化信息后,就设定这个共享数组中的指针指向要发送的消息。为了避免引用到没被定义的指针,主线程将共享数组中的每项先设定为NULL

    2.程序

    2.1程序1.0

    //message1.c
    #include<stdio.h>
    #include<pthread.h>
    #include<stdlib.h>
    #define ll long long
    #define MSG_MAX 233
    #define MAXN 1000
    char* messages[MAXN];
    int thread_count;
    void* Send_msg(void* rank) {
    
    	ll my_rank = (ll) rank;
    	ll dest = (my_rank+1)%thread_count;
    	ll source = (my_rank+thread_count-1)%thread_count;
    	
    	char* my_msg = malloc(MSG_MAX*sizeof(char));
    	
    	sprintf(my_msg,"Hello to %lld from %lld",dest,my_rank);
    	messages[dest] = my_msg;
    
    	if(messages[my_rank] != NULL)//P0
    		printf("Thread %lld > %s
    ",my_rank,messages[my_rank]);//P1
    	else//P2
    		printf("Thread %lld > No message from %lld 
    ",my_rank,source);//P3
    
    //	while(messages[my_rank] == NULL); //Q0
    //	printf("Thread %lld > %s
    ",my_rank,messages[my_rank]);//Q1
    
    	return NULL;
    }
    
    int main(int argc,char* argv[]) {
    
    	ll thread;
    	thread_count = strtol(argv[1],NULL,10);
    	pthread_t* thread_handles = (pthread_t*)malloc(thread_count*sizeof(pthread_t));
    
    	for(thread=0; thread<thread_count; ++thread) {
    		pthread_create(&thread_handles[thread],NULL,Send_msg,(void*)thread);
    	}
    	for(thread=0; thread<thread_count; ++thread) {
    		pthread_join(thread_handles[thread],NULL);
    	}
    
    	free(thread_handles);
    }
    

    当在双核系统上运行多于两个线程的程序时,有些消息始终未收到。例如,在线程t-1把消息复制到message数组前,最先运行的线程0早已结束。

    image-20200325153557505

    这时候,如果把上面代码中的P0~P3部分替换为Q0~Q1部分的话,问题可以得到解决:

    image-20200325153724184

    当然,这个方法有着所有使用忙等待程序都有的问题:耗时。

    2.2程序2.0

    image-20200325154906086

    //message2.c
    #include<stdio.h>
    #include<pthread.h>
    #include<stdlib.h>
    #include<semaphore.h>
    #define ll long long
    #define MSG_MAX 233
    #define MAXN 1000
    char* messages[MAXN];
    int thread_count;
    sem_t* semaphores;
    void* Send_msg(void* rank) {
    
    	ll my_rank = (ll) rank;
    	ll dest = (my_rank+1)%thread_count;
    //	ll source = (my_rank+thread_count-1)%thread_count;
    	
    	char* my_msg = malloc(MSG_MAX*sizeof(char));
    
    	sprintf(my_msg,"Hello to %lld from %lld",dest,my_rank);
    	messages[dest] = my_msg;
    
    	sem_post(&semaphores[dest]);
    
    	sem_wait(&semaphores[my_rank]);
    
    	printf("Thread %lld > %s
    ",my_rank,messages[my_rank]);//Q1
    
    	return NULL;
    }
    
    int main(int argc,char* argv[]) {
    
    	ll thread;
    	thread_count = strtol(argv[1],NULL,10);
    	pthread_t* thread_handles = (pthread_t*)malloc(thread_count*sizeof(pthread_t));
    	semaphores = (sem_t*)malloc(thread_count*sizeof(sem_t));
    
    	for(thread=0; thread<thread_count; ++thread) {
    		sem_init(&semaphores[thread],0,0);
    	}
    	for(thread=0; thread<thread_count; ++thread) {
    		pthread_create(&thread_handles[thread],NULL,Send_msg,(void*)thread);
    	}
    	for(thread=0; thread<thread_count; ++thread) {
    		pthread_join(thread_handles[thread],NULL);
    	}
    	for(thread=0; thread<thread_count; ++thread) {
    		sem_destroy(&semaphores[thread]);
    	}
    
    	free(thread_handles);
    }
    

    image-20200325162202946

    上面的代码不涉及临界区。问题不是一段代码一次只能被一个线程执行,而变成了线程my_rank在线程source发出消息前一直被阻塞。这种一个线程需要等待另一个线程执行某种操作的同步方式,有时称为生产者-消费者同步模型

    3后记

    信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里)。而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这 个资源。比如对全局变量的访问,有时要加锁,操作完了,在解锁。有的时候锁和信号量会同时使用的

  • 相关阅读:
    DevExpress_Winform_使用汇总
    SVN的可视化日志统计工具StatSVN
    jenkins搭建.Net项目自动构建
    关闭tab时,定位到导航栏对应的激活tab
    DevExpress.XtraNavBar.NavBarControl 模拟单击导航的选项事件
    选中Tab控件单击事件,点击tab头,定位到导航栏的选项,为选中状态 ,DevExpress.XtraNavBar.NavBarControl 导航控件定位或选中状态 另导航的选项为选中状态.
    WCF服务部署到IIS问题汇总
    iOS多线程编程
    最新版SDWebImage的使用
    前端入门系列之CSS
  • 原文地址:https://www.cnblogs.com/wangzhebufangqi/p/12567041.html
Copyright © 2020-2023  润新知