在Linux中有两种方法用于处理线程同步:信号量和互斥量。

线程的信号量是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。信号量一般常用于保护一段代码,使其每次只被一个执行线程运行。信号量是用来调协线程对共享资源的访问的。

通过使用信号量可以很好的完成线程同步。两个线程同时监视同一个信号量。A线程增加信号量的值,B线程减少信号量的值。当A线程增加信号量大于0时,B线程的等待信号量就会触发,每触发一次将信号量减1,直到将信号量减为0,B线程继续等待A线程增加信号量。

信号量和互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。

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

信号量:只要信号量的value大于0,其他线程就可以sem_wait成功,成功后信号量的value减1。若value值不大于0,则sem_wait使得线程阻塞,直到sem_post释放后value值加1,但是sem_wait返回之前还是会将此value值减1.

如果信号量的值大于0表示可用的资源数,小于0表示阻塞的线程数。

互斥锁: 只要被锁住,其他任何线程都不可以访问被保护的资源,也就是说,信号量不一定是锁定某一个资源,而是流程上的概念,比如:有A,B两个线程,B线程要等A线程完成某一任务以后再进行自己下面的步骤,这个任务并不一定是锁定某一资源,还可以是进行一些计算或者数据处理之类。而线程互斥量则是“锁住某一资源”的概念,在锁定期间内,其他线程无法对被保护的数据进行操作。在有些情况下两者可以互换。

信号量是一个特殊类型的变量,它可以被增加或减少,但对它的访问都会被保证是原子操作,即使在一个多线程程序中也是如此。也就是说,如果一个程序中有两个或多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。如果换成普通的变量,来自同一个程序中的不同线程的冲突操作将会导致不确定的操作。

两种信号量:二进制信号量和计数信号量。二进制信号量只有0和1两种取值,而计数信号量则有更大的取值范围。如果某个共享资源只能被一个线程访问,那么二进制信号量则是最好的打算;如果有多个线程需要访问共享资源呢,使用计数信号量则是个好的主意。

互斥锁只有0,1两中状态,适合于线程对共享资源的独占访问,很多时候每个资源可以同时被有限的线程访问,此时互斥锁将无法满足;条件变量同步也同样存在这种问题。信号量实际是一种非负整型计数器,可以很好的控制线程之间资源访问,互斥锁能实现的功能,信号量同样可以。

信号量控制资源共享主要是PV原语操作, PV原语是对整数计数器信号量sem的操作。一次P操作使sem减一,而一次V操作使sem加一。进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限。当信号量sem的值大于等于零时,该进程(或线程)具有公共资源的访问权限;相反,当信号量sem的值小于零时,该进程(或线程)就将阻塞直到信号量sem的值大于等于0为止。

信号量的函数都以sem_开头,线程中使用的基本信号量函数有4个,它们都声明在头文件semaphore.h中。

sem_init(sem_t*sem, int pshared, unsigned int value):该函数用于创建信号量。初始化一个定位在sem的匿名信号量。value参数指定信号量的初始值。pshared参数指明信号量是由进程内线程共享,还是由进程之间共享。如果pshared的值为0,那么信号量将被进程内的线程共享,并且应该放置在所有线程都可见的地址上(如全局变量,或者堆上动态分配的变量)。

sem_wait(sem_t*sem):该函数用于以原子操作的方式将信号量的值减1。(原子操作就是,如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰。)但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,这信号量的值将减到1。如果对一个值为0的信号量调用sem_wait(),这个函数就会等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。

sem_post(sem_t*sem):该函数用于以原子操作的方式将信号量的值加1。用来增加信号量的值当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不再阻塞,选择机制同样是由线程的调度策略决定的。它信号量的值加1同时发出信号来唤醒等待的线程。

sem_destroy:该函数用于对用完的信号量的清理。用来释放信号量sem。

下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:

test_thread_sem.cpp:

// reference: https://software.intel.com/zh-cn/blogs/2011/12/02/linux-3
#include <iostream>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>

int g_Flag = 0;
sem_t sem_mutex; // 用于互斥
sem_t sem_syn; // 用于同步

void *thread1(void *arg);
void *thread2(void *arg);
int main()
{
	pthread_t tid1, tid2;
 	int rc1, rc2;

 	sem_init(&sem_mutex, 0, 1);
 	sem_init(&sem_syn, 0, 0);
 	printf(" Inter main !\n");

	 rc2 = pthread_create(&tid2, NULL, thread2, NULL);
 	if(rc2 != 0)
 		printf(" %s, %d \n", __func__, strerror(rc2));

 	rc1 = pthread_create(&tid1, NULL, thread1, &tid2);
 	if(rc1 != 0)
 		printf(" %s, %d \n", __func__, strerror(rc1));

	printf(" Leave main!\n\n");

 	sem_wait(&sem_syn); // 同步等待,阻塞
 	exit(0);
}

void *thread1(void *arg)
{
	pthread_t *ptid = NULL;
	printf(" Enter thread1\n");
 	printf(" thread1 id: %u, g_Flag: %d \n", ( unsigned int )pthread_self(), g_Flag);

 	if(sem_wait( &sem_mutex ) != 0)
	 {
 		perror(" pthread1 sem_mutex\n");
 	}

 	if(g_Flag == 2)
 		sem_post(&sem_syn);

	 g_Flag = 1;

 	if(sem_post( &sem_mutex ) != 0)
 	{
 		perror("pthread1 sem_post\n");
 	}

	printf(" thread1 id: %u, g_Flag: %d \n",( unsigned int )pthread_self(), g_Flag);
	printf(" Leave thread1 \n\n");

 	ptid = (pthread_t*)arg;
 	printf(" ptid = %u \n", *ptid);
 	pthread_join(*ptid, NULL);
 	pthread_exit(0 );
}

void *thread2(void *arg)
{
 	printf(" Enter thread2 !\n");
 	printf(" thread2 id: %u , g_Flag: %d \n", ( unsigned int)pthread_self(), g_Flag);

 	if(sem_wait(&sem_mutex) != 0)
 	{
 		perror("thread2 sem_wait \n");
 	}

 	if(g_Flag == 1)
 		sem_post(&sem_syn);

 	g_Flag = 2;

 	if(sem_post( &sem_mutex ) != 0)
 	{
 		perror(" thread2 sem_post\n");
 	}

 	printf(" thread2 id: %u , g_Flag: %d \n", (unsigned int)pthread_self(), g_Flag);
 	printf("Leave thread2 \n\n");

 	pthread_exit(0);
}

test_thread_sem1.cpp:

// reference: http://man7.org/linux/man-pages/man3/sem_wait.3.html

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>

sem_t sem;

#define handle_error(msg) \
   do { perror(msg); exit(EXIT_FAILURE); } while (0)

static void handler(int sig)
{
	write(STDOUT_FILENO, "sem_post() from handler\n", 24);
   	if (sem_post(&sem) == -1) {
	   	write(STDERR_FILENO, "sem_post() failed\n", 18);
	   	_exit(EXIT_FAILURE);
   	}
}

int main(int argc, char *argv[])
{
   	struct sigaction sa;
   	struct timespec ts;
   	int s;

   	if (argc != 3) {
	   	fprintf(stderr, "Usage: %s <alarm-secs> <wait-secs>\n", argv[0]);
	   	exit(EXIT_FAILURE);
   	}

	if (sem_init(&sem, 0, 0) == -1)
	   	handle_error("sem_init");

   	/* Establish SIGALRM handler; set alarm timer using argv[1] */
   	sa.sa_handler = handler;
   	sigemptyset(&sa.sa_mask);
   	sa.sa_flags = 0;
   	if (sigaction(SIGALRM, &sa, NULL) == -1)
	   	handle_error("sigaction");

   	alarm(atoi(argv[1]));

   	/* Calculate relative interval as current time plus
	  number of seconds given argv[2] */

   	if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
	   	handle_error("clock_gettime");

   	ts.tv_sec += atoi(argv[2]);

   	printf("main() about to call sem_timedwait()\n");
   	while ((s = sem_timedwait(&sem, &ts)) == -1 && errno == EINTR)
	   	continue;       /* Restart if interrupted by handler */

   	/* Check what happened */

   	if (s == -1) {
	   	if (errno == ETIMEDOUT)
		   	printf("sem_timedwait() timed out\n");
	   	else
		   	perror("sem_timedwait");
   	} else
	   	printf("sem_timedwait() succeeded\n");

   	exit((s == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}

// ./test_thread_sem1 2 3
// ./test_thread_sem1 2 1

test_thread_sem2.cpp:

// reference: https://mahaveerdarade.wordpress.com/2013/09/16/semaphores-in-linux-sem_wait-sem_post-code-examples-in-c/
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>

int cnt = 0;
int a[]={1,2,3,4,5,6,7,8,9};
char arr[]={'a','b','c','d','e','f','g','h','j'};
sem_t s1;

void* pc(void* arg)
{
	int i=0;
	while(i<9) {
		sem_wait(&s1);
		while(cnt==0) {
			sem_post(&s1);
		}
		printf("%c", arr[i++]);
		cnt=0;
		sem_post(&s1);
	}
}

void* pi(void* arg)
{
	int i=0;
	while(i<9) {
		sem_wait(&s1);
		while(cnt==1) {
			sem_post(&s1);
		}
		printf("%d",a[i++]);
		cnt=1;
		sem_post(&s1);
	}
}

int main() {
	pthread_t t1,t2;
	sem_init(&s1, 0, 1);
	pthread_create(&t1, NULL, pc, NULL);
	pthread_create(&t2, NULL, pi, NULL);
	pthread_join(t1, NULL);
	pthread_join(t2, NULL);
	sem_destroy(&s1);
	return 0;
}

test_thread_sem3.cpp:

// reference: http://www.amparo.net/ce155/sem-ex.html
/* Includes */
#include <unistd.h>     /* Symbolic Constants */
#include <sys/types.h>  /* Primitive System Data Types */
#include <errno.h>      /* Errors */
#include <stdio.h>      /* Input/Output */
#include <stdlib.h>     /* General Utilities */
#include <pthread.h>    /* POSIX Threads */
#include <string.h>     /* String handling */
#include <semaphore.h>  /* Semaphore */

/* prototype for thread routine */
void* handler(void* ptr);

/* global vars */
/* semaphores are declared global so they can be accessed
   in main() and in thread routine,
   here, the semaphore is used as a mutex */
sem_t mutex;
int counter; /* shared variable */

int main()
{
    int i[2];
    pthread_t thread_a;
    pthread_t thread_b;

    i[0] = 0; /* argument to threads */
    i[1] = 1;

    sem_init(&mutex, 0, 1);      /* initialize mutex to 1 - binary semaphore */
                                 /* second param = 0 - semaphore is local */

    /* Note: you can check if thread has been successfully created by checking return value of
       pthread_create */
    pthread_create(&thread_a, NULL, handler, (void*)&i[0]);
    pthread_create(&thread_b, NULL, handler, (void*)&i[1]);

    pthread_join(thread_a, NULL);
    pthread_join(thread_b, NULL);

    sem_destroy(&mutex); /* destroy semaphore */

    /* exit */
    exit(0);
} /* main() */

void* handler(void* ptr)
{
    int x;
    x = *((int *)ptr);
    printf("Thread %d: Waiting to enter critical region...\n", x);
    sem_wait(&mutex);       /* down semaphore */
    /* START CRITICAL REGION */
    printf("Thread %d: Now in critical region...\n", x);
    printf("Thread %d: Counter Value: %d\n", x, counter);
    printf("Thread %d: Incrementing Counter...\n", x);
    counter++;
    printf("Thread %d: New Counter Value: %d\n", x, counter);
    printf("Thread %d: Exiting critical region...\n", x);
    /* END CRITICAL REGION */
    sem_post(&mutex);       /* up semaphore */

    pthread_exit(0); /* exit thread */
}

GitHubhttps://github.com/fengbingchun/Linux_Code_Test

Linux下多线程编程中信号量介绍及简单使用的更多相关文章

  1. Linux下多线程编程遇到的一些问题

    今天在学习了Linux的多线程编程的基础的知识点.于是就试着做了一个简单的Demo.本以为会得到预期的结果.不成想却遇到了意想不到的问题. 代码展示 我的C 代码很简单,就是一个简单的示例程序,如下: ...

  2. Linux下多线程编程-信号量

    今天来谈谈线程的同步--信号量. 首先来看看一些概念性的东西: 如进程.线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行:B依言执行,再将结果给 ...

  3. Linux下多线程编程

    一.为什么要引入线程? 使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式.在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维 ...

  4. 转载自~浮云比翼:Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

    Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)   介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可 ...

  5. Linux下的编程实战【转】

    一篇比较不错的文章, 降到了 makefile make , gcc编译器,GDB调试器, Linux文件系统,Linux文件API,.C语言库函数(C库函数的文件操作实际上是独立于具体的操作系统平台 ...

  6. [转]Linux 的多线程编程的高效开发经验

    Linux 平台上的多线程程序开发相对应其他平台(比如 Windows)的多线程 API 有一些细微和隐晦的差别.不注意这些 Linux 上的一些开发陷阱,常常会导致程序问题不穷,死锁不断.本文中我们 ...

  7. Linux 的多线程编程的高效开发经验(转)

    http://www.ibm.com/developerworks/cn/linux/l-cn-mthreadps/ 背景 Linux 平台上的多线程程序开发相对应其他平台(比如 Windows)的多 ...

  8. Linux 的多线程编程的高效开发经验

    http://www.ibm.com/developerworks/cn/linux/l-cn-mthreadps/ 背景 Linux 平台上的多线程程序开发相对应其他平台(比如 Windows)的多 ...

  9. Java多线程编程(2)--多线程编程中的挑战

    一.串行.并发和并行   为了更清楚地解释这三个概念,我们来举一个例子.假设我们有A.B.C三项工作要做,那么我们有以下三种方式来完成这些工作:   第一种方式,先开始做工作A,完成之后再开始做工作B ...

随机推荐

  1. linux smem 查看各进程使用memory情况

    SMEM(8) SMEM(8) NAME smem - Report memory usage with shared memory divided proportionally. SYNOPSIS ...

  2. The Master of Science degree in Computer Scienc

    Computer Science MS Degree MS Degree or Depth       45.00 Hours Required   http://scpd.stanford.edu/ ...

  3. 单纯形算法 matlab

    %单纯形 %目标函数标准化 % min x1-3x2+2x3 %输入参量 N=[3 -1 2;-2 4 0;-4 3 8]; B=eye(3); A=[N B]; cn=[1;-3;2]; cb=ze ...

  4. ajax本地跨域请求以及解决方法

    什么是跨域?   我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景.所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源,只要没 ...

  5. Eclipse validation

    window->preferences->validation 可以取消部分文件的验证,取消build时验证,改为手动验证,提高效率.

  6. 多线程操作C++ STL vector出现概率coredump问题及尽量避免锁的双缓冲队列

    多线程操作全局变量,必须考虑同步问题,否则可能出现数据不一致, 甚至触发coredump. 前段时间, 遇到一个多线程操作了全局的vector的问题,  程序崩了.场景是这样的:某全局配置参数保存在一 ...

  7. CVE-2017-8046 复现与分析

    环境搭建 使用的项目为https://github.com/spring-guides/gs-accessing-data-rest.git里面的complete,直接用IDEA导入,并修改pom.x ...

  8. mongodb启动与运用

    在操作前需要启动mongodb数据库服务 1.首先打开dos窗口,然后选择路径到你的安装路径下的bin目录(我的路径是的D:mongo\mongodb\bin) 2.然后输入启动命令(D:mongo\ ...

  9. Linux服务器之间免密同步文件、重启R服务

    机器:ML-01/ML-02/ML-03 需求: 1.在ML-01上自动将文件同步至ML-02/ML-03 2.在ML-01上通过脚本重启ML-02/ML-03上的R服务 说明:以下示例中,ML-02 ...

  10. ejs引擎项目

    关于这个我也很懵逼,写这篇博客就是想记录一下,有哪位大神看到之后可以略微指点一二,不胜感激....... 一.项目结构 db model user.js version.js schema xx.js ...