005_ipc概述及信号量

【信号量】

在Linux系统中,信号量主要用于进程间的同步。Linux提供了两种类型的信号量:POSIX信号量和System V信号量,信号量(Semaphore)是一种同步机制,用于多线程或进程间的同步和互斥。信号量可以控制对共享资源的访问,防止多个线程或进程同时访问同一资源,导致数据不一致或竞态条件POSIX信号量是线程和进程都可以使用的,而System V信号量主要用于进程间通信

信号量的主要类型有:

  1. 二值信号量(Binary Semaphore):只有两个值,0 和 1。它可以实现互斥锁的功能,当一个线程或进程获得信号量时,其他线程或进程必须等待,直到信号量被释放。

  2. 计数信号量(Counting Semaphore):可以有多个值,通常用于限制对共享资源的访问数量。当一个线程或进程获得信号量时,信号量的值会减一;当释放信号量时,信号量的值会加一。当信号量的值为 0 时,其他线程或进程必须等待。

信号量的操作主要有两种:

  1. P(Wait)操作:当一个线程或进程需要访问共享资源时,它会执行 P 操作。如果信号量的值大于 0,线程或进程会获得信号量,并将信号量的值减一;如果信号量的值为 0,线程或进程会阻塞,直到信号量被释放。

  2. V(Signal)操作:当一个线程或进程完成对共享资源的访问时,它会执行 V 操作。这将释放信号量,并将信号量的值加一。如果有线程或进程在等待信号量,其中一个将被唤醒。

信号量在多线程编程中非常有用,可以解决许多并发问题,如生产者-消费者问题、读者-写者问题等。然而,不当使用信号量可能会导致死锁、饥饿等问题,因此需要谨慎使用。

Linux支持两种类型的信号量:

  1. System V 信号量:这是传统的信号量实现,支持信号量的集合,每个信号量可以独立地设置和操作。
  2. POSIX 信号量:这是基于POSIX标准的信号量实现,通常更易于使用和理解。

System V 信号量

System V 信号量的基本操作包括:

  • semget:创建或获取一个信号量集。
  • semop:对信号量集中的信号量执行操作(P操作和V操作)。
  • semctl:对信号量集进行控制操作,如初始化信号量的值

POSIX 信号量

POSIX 信号量通常更容易使用,因为它提供了更简单的API。

我们再看看其他技术人员所做的比较全面的阐述,如下:

为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的

信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。

工作原理:

由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:

P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行

V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行

在Linux中,信号量的操作涉及到多个函数,这些函数根据信号量的类型(System V 或 POSIX)而有所不同。声明在头文件sys/sem.h中,以下是这些函数的概述:

System V 信号量

System V 信号量涉及到以下几个主要函数:

semget

    作用: 创建或获取一个信号量集

  • semget 是一个用于创建或获取一个信号量集的 Linux 系统调用。它主要用于 System V 信号量
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);

参数

  • key:一个键值,用于标识信号量集。通常通过 ftok 函数生成。
  • nsems:信号量集中的信号量数量。
  • semflg:权限标志,包括信号量的权限(如 0666)和创建标志(如 IPC_CREAT)。

返回值

  • 成功时,返回信号量集的标识符。
  • 失败时,返回 -1,并设置 errno 以指示错误。

错误码

  • EACCES:没有足够的权限访问信号量集。
  • EEXISTIPC_CREAT 被指定,但信号量集已存在。
  • EINVALnsems 小于0或大于 SEMMSL
  • ENOENTIPC_CREAT 未被指定,且信号量集不存在。
  • ENOSPC:系统范围内的信号量集数量已达到上限。
  • ENOSYS:系统不支持 semget 系统调用

【详解】

第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget()函数并提供一个键,再由系统生成一个相应的信号标识符(semget()函数的返回值),只有semget()函数才直接使用信号量键,所有其他的信号量函数使用由semget()函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。

第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。

第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。

semget()函数成功返回一个相应信号标识符(非零),失败返回-1.

注:每个函数的【详解】部分都是参考下面链接

 Linux进程间通信(五):信号量 semget()、semop()、semctl() - 52php - 博客园

semop

   作用: 对信号量集中的信号量执行操作(P操作和V操作)

  • semop 是一个用于对信号量集执行操作的 Linux 系统调用。它主要用于 System V 信号量
#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, unsigned nsops);

参数

  • semid:信号量集的标识符,通常通过 semget 函数获取。
  • sops:指向 sembuf 结构体的指针,该结构体描述了要执行的操作。
  • nsopssops 指针指向的 sembuf 结构体的数量。

返回值

  • 成功时,返回 0。
  • 失败时,返回 -1,并设置 errno 以指示错误。

错误码

  • EFAULTsops 指针指向的地址无效。
  • EIDRM:信号量集已被删除。
  • EINTR:在执行操作时收到信号。
  • EINVALsemid 无效,sops 无效,或 nsops 无效。
  • EIPCERM:信号量集已被删除。
  • EACCES:没有足够的权限执行操作。
  • EAGAIN:操作无法立即完成,因为信号量的值小于 sembuf 结构体中的 sem_op 字段。

sembuf 结构体

sembuf 结构体用于描述对信号量执行的操作,其定义如下:

struct sembuf {
    unsigned short sem_num;  // 信号量集中的信号量编号
    short sem_op;            // 操作类型,正值表示增加,负值表示减少
    short sem_flg;           // 操作标志,如 `IPC_NOWAIT`,`SEM_UNDO`
};
struct sembuf{
    short sem_num; // 除非使用一组信号量,否则它为0
    short sem_op;  // 信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
                   // 一个是+1,即V(发送信号)操作。
    short sem_flg; // 通常为SEM_UNDO,使操作系统跟踪信号,
                   // 并在进程没有释放该信号量而终止时,操作系统释放信号量
};

上面两个结构是一模一样的,只不过第二个备注更地道一些

semctl

    作用: 对信号量集进行控制操作,如初始化信号量的值

  • semctl 是 Linux 系统中的一个重要系统调用,用于对信号量集或信号集中的某个信号执行控制操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);

参数说明

  • semid:信号量集合的标识符,通常由 semget 函数返回。
  • semnum:集合中信号量的编号。如果操作整个信号量集合,则设置为 0。
  • cmd:需要执行的命令,有效值包括:
    • IPC_STAT:将与 semid 关联的内核数据结构拷贝到由 arg.buf 指针指向的内存区。
    • IPC_SET:将由 arg.buf 指针指向的 semidds 的一些成员写入相关联的内核数据结构,同时更新它的 semctime 成员。
    • IPCRMID:立即删除信号集,唤醒所有被阻塞的进程。
    • IPC_INFO:返回系统范围内关于信号集的制约和其他参数,并存放在 arg.buf 指向的内存区。
    • GETVAL:获取信号量的当前值。
    • SETVAL:设置信号量的值,初始化要用的命令。
    • GETALL:获取信号集合的地址传递给第四个参数。
    • GETNCNT:返回等待信号量值递增的进程数。
    • GETZCNT:返回等待信号量值递减的进程数。
    • SETALL:将第四个参数传递给所有信号量
//如果有第四个参数,它通常是一个union semum结构,定义如下:

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *arry;
};
//前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个

//SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在
//信号量第一次使用前对它进行设置。

//IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

ftok

    作用: 生成一个唯一的键值,用于semget函数

  • ftok 是一个在 Linux 和 Unix 系统中用于生成一个唯一的键值的系统调用,这个键值可以用于创建共享资源,如消息队列、信号量集或共享内存。这个键值是基于一个现有的文件名和一个项目ID生成的
#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *path, int proj_id);

参数说明

  • path:一个现有的文件路径名,这个文件不需要存在,但是必须能够被访问。
  • proj_id:一个8位的整数,用于与文件名结合生成唯一的键值。

返回值

  • 成功时,ftok 函数返回一个唯一的键值。
  • 失败时,返回 -1,并设置 errno 以指示错误。

注意事项

  • ftok 函数生成的键值在同一系统上是唯一的,但在不同的系统上可能会生成相同的键值。
  • ftok 函数生成的键值并不保证是唯一的,因为如果两个不同的文件路径和项目ID组合起来产生了相同的键值,那么它们将指向同一个共享资源。
  • ftok 函数生成的键值通常用于 msggetsemget 或 shmget 系统调用,以创建共享资源

testcode

System V信号量是一种用于进程间同步的机制,它允许多个进程共享一组信号量,并通过这些信号量来协调对共享资源的访问。以下是一个使用System V信号量的C语言示例,演示了如何使用信号量来同步两个进程对共享资源的访问。

首先,我们需要创建一个信号量集,并为该信号量集中的信号量初始化值。然后,两个进程将使用这些信号量来同步对共享资源的访问。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include "./head/log.h"
#include <stdlib.h>
#include <fcntl.h>//open  creat  fcntl
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <stdlib.h>


/*semaphore:信号量*/

/*union semun对象,一般作为semctl函数的第四个参数*/
union semun {
	int val;
	struct semid_ds *buf;
	unsigned short *array;
};

/*初始化信号量*/
int init_semaphore(int sem_id, int sem_num, int init_value)
{
	union semun sem_union;
	
	sem_union.val = init_value;
	if(semctl(sem_id, sem_num, SETVAL, sem_union) == -1)
	{
		LOGS("semctl is ERROR!");
		return FAIL;
	}
	return SUCCESS;
}

/*P操作*/
int sem_P(int sem_id, int sem_num)
{
	struct sembuf sem_b;

	sem_b.sem_num = sem_num;
	sem_b.sem_op = -1;
	sem_b.sem_flg = SEM_UNDO;
	if(semop(sem_id, &sem_b, 1) == -1)
	{
		LOGS("sem_P is coming and semop is ERROR!");
		return FAIL;
	}
	return SUCCESS;
}

/*V操作*/
int sem_V(int sem_id, int sem_num)
{
	struct sembuf sem_b;

	sem_b.sem_num = sem_num;
	sem_b.sem_op = 1;
	sem_b.sem_flg = SEM_UNDO;
	if(semop(sem_id, &sem_b, 1) == -1)
	{
		LOGS("sem_V is coming andm semop is ERROR!");
		return FAIL;
	}
	return SUCCESS;
}

/*test_play*/
int test_systemV()
{
	key_t key;
	int sem_id;
	int i;

	//1.获取信号量键
	key = ftok("semfile", 65);

	//2.创建信号量
	sem_id = semget(key, 1, 0666 | IPC_CREAT);

	//3.初始化信号量
	init_semaphore(sem_id, 0, 1);

	//4.创建子进程
	pid_t pid = fork();
	if(pid == -1)
	{
		LOGS("FORK is ERROR!");
		exit(FAIL);
	}

	if(pid == 0)
	{//子进程
		for(i = 0; i < 3; i++)
		{
			sem_P(sem_id, 0);
			LOGS("Child process is using the resource!");
			sleep(1);
			sem_V(sem_id, 0);
		}
		LOGS("OUT Child process!!!");
	}
	else
	{//父进程
		for(i = 0; i < 3; i++)
		{
			sem_P(sem_id, 0);
			LOGS("Parent process is using the resource!");
			sleep(1);
			sem_V(sem_id, 0);
		}
		LOGS("OUT Parent process!!!");
	}
	
	LOGS("Del semaphore!!!");
	//删除信号量集
	semctl(sem_id, 0, IPC_RMID, NULL);

	return SUCCESS;
}
	

int main()
{
	LOGS("========================zll_debug_start===========================");
	test_systemV();
	LOGS("========================zll_debug_end=============================");

	return SUCCESS;
}

输出如下:

在这个例子中,我们创建了一个信号量集,其中包含一个信号量,并初始化其值为1。然后,我们创建了两个进程(父进程和子进程),它们通过信号量来同步对共享资源的访问。每个进程在访问资源之前执行P操作(减少信号量的值),在访问资源之后执行V操作(增加信号量的值)。这样,就确保了在任何时候只有一个进程能够访问共享资源。

请注意,这个例子只是为了演示System V信号量的基本使用,并没有涉及到更复杂的同步场景。在实际应用中,可能需要使用多个信号量来协调多个进程对多个共享资源的访问。

POSIX 信号量

POSIX 信号量涉及到以下几个主要函数:

sem_open

        作用:打开或创建一个命名信号量。

sem_open 是一个用于创建或打开命名信号量的函数,它在多进程编程中常用于同步。以下是关于 sem_open 函数的一些关键信息:

  1. 函数原型

    sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
    
    • name:信号量的名字,用于在多个进程间共享。
    • oflag:操作标志,可以是0、O_CREAT(创建信号量)或O_CREAT|O_EXCL(如果没有指定信号量则创建)。
    • mode:权限位,指定信号量的权限。
    • value:信号量的初始值。
  2. 返回值

    • 成功时返回指向信号量的指针。
    • 出错时返回SEM_FAILED。
  3. 功能

    • 如果信号量不存在且指定了O_CREAT,则创建并初始化信号量。
    • 如果信号量已存在,则直接打开它。

      错误处理

  • 常见错误包括信号量名称不正确、权限问题等。
  • 可以通过检查返回值是否为SEM_FAILED来判断是否出错,并使用相应的错误处理机制

sem_close

        作用:关闭一个信号量。

sem_close 是一个用于关闭信号量的函数,它在多进程编程中用于释放与信号量相关的资源。以下是关于 sem_close 函数的一些关键信息:

int sem_close(sem_t *sem);
  • sem:指向要关闭的信号量的指针。

功能

sem_close 函数用于关闭一个通过 sem_open 创建或打开的命名信号量。当进程不再需要访问信号量时,应调用此函数以释放相关资源。

返回值

  • 成功时,返回 0。
  • 失败时,返回 -1,并设置 errno 以指示错误。

常见错误

  • EINVALsem 不是一个有效的信号量。
  • EFAULTsem 指向的地址无效

注意事项

  • sem_close 只影响调用它的进程,信号量本身仍然存在,直到所有引用它的进程都调用了 sem_close
  • 如果需要完全删除信号量,应使用 sem_unlink 函数。

与 sem_unlink 的区别

  • sem_close:关闭进程对信号量的引用。
  • sem_unlink:删除命名信号量,当所有引用该信号量的进程都关闭了它后,信号量将被销毁

sem_unlink

        作用: 删除一个命名信号量。

sem_unlink 是一个用于删除命名信号量的函数,它在多进程编程中用于从系统中移除信号量。以下是关于 sem_unlink 函数的一些关键信息:

int sem_unlink(const char *name);
  • name:指向要删除的命名信号量的名称的指针。

功能

sem_unlink 函数用于删除一个通过 sem_open 创建的命名信号量。信号量的名称会立即从系统中移除,但信号量本身只有在所有引用它的进程都调用 sem_close 后才会被销毁。

返回值

  • 成功时,返回 0。
  • 失败时,返回 -1,并设置 errno 以指示错误。

常见错误

  • EACCES:调用者没有权限删除此信号量。
  • ENAMETOOLONG:信号量名称过长。
  • ENOENT:没有找到指定名称的信号量。

与 sem_close 的区别

  • sem_close:关闭进程对信号量的引用,信号量仍然存在。
  • sem_unlink:从系统中删除信号量的名称,信号量将在所有引用它的进程都关闭后销毁

sem_wait

        作用:等待信号量(P操作)。

sem_wait 是一个用于等待(即减一)信号量的函数,它在多线程或多进程同步中常用于实现互斥和同步。以下是关于 sem_wait 函数的一些关键信息:

int sem_wait(sem_t *sem);
  • sem:指向信号量对象的指针。

功能

sem_wait 函数用于减少(即“等待”或“获取”)信号量的值。如果信号量的值大于0,它会立即减一,并返回。如果信号量的值为0,调用线程会被阻塞,直到信号量的值变为大于0,然后减一并返回。

返回值

  • 成功时,返回 0。
  • 失败时,返回 -1,并设置 errno 以指示错误。

常见错误

  • EINTR:调用被信号中断。
  • EINVALsem 不是一个有效的信号量。
  • ETIMEDOUT:(仅限 sem_timedwait)指定的等待时间超时。

注意事项

  • sem_wait 是一个阻塞调用,如果信号量的值不为正,调用线程将阻塞。
  • 在多进程环境中,应使用命名信号量(通过 sem_open 创建)而不是未命名信号量(通过 sem_init 创建)。
  • 为了避免死锁,确保信号量的获取和释放总是成对出现。

sem_post

       作用 :释放信号量(V操作)。

sem_post 函数用于增加一个命名或未命名信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中一个线程不再阻塞,从而继续执行。这个函数主要用于提高进程之间的并发性,保证进程的正确执行顺序

#include <semaphore.h>
int sem_post(sem_t *sem);

其中,sem 是指向待操作信号量的指针。函数成功返回0,失败返回-1。

函数特点:

信号量值加一:每次调用sem_post函数,都会使信号量的值加一。当信号量的值等于1时,它将可以通过sem_wait函数来减一。因此,sem_post函数可以用于实现进程的同步和通信。

只能对二元信号量进行操作sem_post函数只能对二元信号量进行操作。

sem_init

        作用:初始化一个非命名信号量。

sem_init 函数是Linux系统中用于初始化一个未命名的信号量的函数。信号量是一种用于同步进程之间操作的机制,它可以保证多个进程或线程在访问共享资源时不会发生冲突

sem_init 函数用于初始化一个未命名的信号量。这个函数主要用于在进程内部创建信号量,用于进程内的线程同步

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

其中:

  • sem 是指向信号量的指针。
  • pshared 用于指定信号量的共享方式。如果设置为0,则信号量仅在当前进程的线程之间共享;如果设置为非0,则信号量可以在多个进程之间共享。
  • value 是信号量的初始值。

函数成功返回0,失败返回-1。

函数特点:

初始化信号量sem_init函数用于初始化一个未命名的信号量,使其可以用于进程内的线程同步。

信号量值value参数用于设置信号量的初始值,通常设置为1或0。

sem_destroy

        作用:销毁一个非命名信号量。

sem_destroy 函数是Linux系统中用于销毁一个未命名的信号量的函数。当不再需要使用信号量时,应该使用该函数来释放信号量所占用的资源

sem_destroy 函数用于销毁一个未命名的信号量。这个函数主要用于在进程内部销毁信号量,释放信号量所占用的资源。

#include <semaphore.h>
int sem_destroy(sem_t *sem);

其中:

  • sem 是指向信号量的指针。

函数成功返回0,失败返回-1。

函数特点:

销毁信号量sem_destroy函数用于销毁一个未命名的信号量,释放信号量所占用的资源。

线程安全:在使用sem_destroy函数之前,需要确保没有线程正在等待或操作该信号量

testcode

POSIX信号量是一种用于多线程编程中的同步机制,用于控制对共享资源的访问。以下是一个使用POSIX信号量的简单案例,演示了如何使用信号量来同步两个线程对共享资源的访问。

假设有两个线程,线程A和线程B。它们都需要访问一个共享资源(例如一个共享变量)。为了防止竞态条件,我们可以使用信号量来同步这两个线程的访问。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include "./head/log.h"
#include <stdlib.h>
#include <fcntl.h>//open  creat  fcntl
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <stdlib.h>
#include <pthread.h>//线程头文件
#include <semaphore.h>//信号量头文件


/*POSIX信号量操作*/
//1.创建共享资源
int shared_resource = 0;
//2.定义信号量
sem_t sem;

//线程A的任务
void* thread_a(void* arg)
{
	for(int i = 0; i < 5; i++)
	{
		//等待信号量
		sem_wait(&sem);
		//访问共享资源
		shared_resource++;
		printf("Thread A: %d\n", shared_resource);
		//释放信号量
		sem_post(&sem);
		//模拟耗时操作
		sleep(1);
	}
	return NULL;
}
//线程B的任务
void* thread_b(void *arg)
{
	for(int i = 0; i < 5; i++)
	{
		//等待信号量
		sem_wait(&sem);
		//访问共享资源
		shared_resource++;
		printf("Thread B: %d\n", shared_resource);
		//释放信号量
		sem_post(&sem);
		//模拟耗时操作
		sleep(1);
	}
	return NULL;
}
int test_POSIX()
{
	//3.初始化信号量
	sem_init(&sem, 0, 1);
	//4.创建线程
	pthread_t t1, t2;
	pthread_create(&t1, NULL, thread_a, NULL);
	pthread_create(&t2, NULL, thread_b, NULL);
	//5.等待线程结束
	pthread_join(t1, NULL);
	pthread_join(t2, NULL);
	//6.销毁信号量
	sem_destroy(&sem);

	return SUCCESS;
}
	

int main()
{
	LOGS("========================zll_debug_start===========================");
	test_POSIX();
	LOGS("========================zll_debug_end=============================");

	return SUCCESS;
}

代码解释:

  1. 共享资源:定义了一个共享资源shared_resource
  2. 信号量:定义了一个信号量sem
  3. 线程A的任务:线程A通过sem_wait等待信号量,然后访问共享资源,最后通过sem_post释放信号量。
  4. 线程B的任务:线程B通过sem_wait等待信号量,然后访问共享资源,最后通过sem_post释放信号量。
  5. 主函数:初始化信号量,创建线程,等待线程结束,销毁信号量。

运行结果如下:

运行上述代码,可以看到线程A和线程B交替访问共享资源,每次访问后共享资源的值都会增加1

注意:

在编译的时候,记得makefile中加入如下字样:

注意事项

  • 信号量是一种同步机制,用于控制多个进程对共享资源的访问。
  • System V 信号量支持信号量的集合,而 POSIX 信号量通常更易于使用和理解。
  • 使用信号量时,需要注意初始化、操作和销毁信号量的正确顺序,以避免死锁和资源泄露。

信号量还有哪些应用场景?

信号量在多线程和多进程编程中有着广泛的应用场景。以下是一些常见的应用场景:

  1. 资源池管理

    • 信号量可以用来控制对有限资源的访问,比如数据库连接池、文件句柄或者网络连接。每个资源都可以用一个信号量来表示,信号量的计数表示当前可用的资源数量。当一个线程或进程需要使用资源时,它必须首先获取信号量(即等待信号量),使用完毕后释放信号量(即释放信号量)。
  2. 生产者-消费者问题

    • 在生产者-消费者问题中,生产者生成数据,消费者消费数据。信号量可以用来同步生产者和消费者的行为,确保生产者在消费者消费完之前不会覆盖数据,同时消费者在数据准备好之前不会尝试消费。
  3. 读者-写者问题

    • 读者-写者问题涉及到多个线程或进程对共享数据的访问,其中读者只读取数据,写者修改数据。信号量可以用来确保在写者写入数据时没有读者读取,以及在读者读取时没有写者写入。
  4. 任务队列

    • 在任务队列中,工作线程从队列中取出任务执行。信号量可以用来同步任务队列的访问,确保工作线程在执行任务时队列不为空,同时任务生成者在添加任务时队列不会溢出。
  5. 互斥访问共享数据

    • 信号量可以用作互斥锁(mutex),确保一次只有一个线程或进程可以访问共享数据,防止竞态条件。
  6. 事件通知

    • 信号量可以用来实现事件通知机制,一个线程或进程可以等待某个事件的发生(通过等待信号量),而另一个线程或进程在事件发生时通知等待者(通过释放信号量)。
  7. 进程间同步

    • 在多进程环境中,信号量可以用来同步不同进程之间的执行,确保它们按照一定的顺序执行或者等待某些条件满足。
  8. 死锁避免

    • 信号量可以用来帮助避免死锁,通过合理地分配和释放信号量,可以确保系统资源得到有效利用,避免死锁的发生。

这些应用场景展示了信号量作为一种同步机制在并发编程中的重要性。通过使用信号量,程序员可以有效地管理线程和进程之间的交互,确保程序的稳定性和正确性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/914842.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SSA-CNN-LSTM-MATT多特征分类预测

项目源码获取方式见文章末尾&#xff01; 600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【基于CNN-RNN的影像报告生成】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生成】 4.【CNN模型实现…

有手就会:java 环境变量配置 - 包含windows、macos、linux和vscode的详细配置步骤

java 环境变量配置 本文旨在帮助用户完成Java环境变量的配置&#xff0c;涵盖Windows、Linux和macOS三大操作系统。对于每个系统&#xff0c;不仅提供了通过命令行设置环境变量的方法&#xff0c;还介绍了如何在VSCode中进行相应配置以启动Java项目&#xff0c;确保开发者能够…

Error response from daemon:

指出在尝试解析 auth.docker.io&#xff08;Docker Hub 的一个域名&#xff0c;用于身份验证和镜像拉取&#xff09;时&#xff0c;DNS 查询超时了。这通常意味着你的 Docker 客户端无法通过配置的 DNS 服务器&#xff08;在这个案例中是 &#xff09;来解析域名 解决方案&…

127.WEB渗透测试-信息收集-ARL(18)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a; 这一行是对应使用的指纹 这个界面是springbot 如果存在漏洞&#xff0c;他的信息里面可…

字节、快手、Vidu“打野”升级,AI视频小步快跑

文&#xff5c;白 鸽 编&#xff5c;王一粟 继9月份版本更新之后&#xff0c;光锥智能从生数科技联合创始人兼CEO唐家渝朋友圈获悉&#xff0c;Vidu大模型将于本周再次进行版本升级&#xff0c;Vidu-1.5版本即将上线。 此版本更新方向仍是重点延伸大模型的泛化能力和主体…

分享 pdf 转 word 的免费平台

背景 找了很多 pdf 转 word 的平台都骗进去要会员&#xff0c;终于找到一个真正免费的&#xff0c;遂分享。 网址 PDF转Word转换器 - 100%免费市面上最优质的PDF转Word转换器 - 免费且易于使用。无附加水印 - 快速将PDF转成Word。https://smallpdf.com/cn/pdf-to-word

CentOS下如何安装Nginx

1、下载nginx 官方网站 http://nginx.org 下载链接&#xff1a;http://nginx.org/download/ 下载完成后的安装包&#xff1a; 2、使用解压命令进行解压 tar -zxvf nginx-1.13.7.tar.gz3、在安装所需的安装环境 安装gcc环境 yum install gcc-c安装第三方开发包 - PCRE(P…

Springboot 不同版本的配置文件怎么知道差异

起因 今天配置一个 Springboot-3.3.5 的 redis-starter&#xff0c;结果一直提示链接不上 redis java.net.ConnectException: Connection refused我反复对比了新项目和老项目的 redis 配置文件格式&#xff0c;是一模一样的&#xff01; Debug 过程 配置中增加了如下配置 …

Diffusion Policy——斯坦福机器人UMI所用的扩散策略:从原理到其编码实现(含Diff-Control、ControlNet详解)

前言 本文一开始是属于此文《UMI——斯坦福刷盘机器人&#xff1a;从手持夹持器到动作预测Diffusion Policy(含代码解读)》的第三部分&#xff0c;考虑后Diffusion Policy的重要性很高&#xff0c;加之后续还有一系列基于其的改进工作 故独立成本文&#xff0c;且写的过程中 …

计算机新手练级攻略——写博客

目录 计算机新手练级攻略——写博客计算机新手写博客的好处加深知识点建立个人IP可能有额外的收入 如何写博客确定博客主题方向选择博客平台学习基础技能一定要有互动性持之以恒&#xff0c;克服惰性Just do it&#xff01;&#xff01;&#xff01; 计算机新手练级攻略——写博…

哥德巴赫猜想渐行渐远

我现在的工作&#xff0c;表明经典分析可能出了问题&#xff0c;如此则连Vinogradov的三素数定理都不成立了&#xff0c;更别说基于L-函数方程的陈氏定理“12”了。事实上即使L-函数方程成立&#xff0c;由于我指出Siegel定理不成立&#xff0c;陈景润和张益唐的工作就不成立。…

Linux探秘坊-------1.系统核心的低语:基础指令的奥秘解析(1)

1.Linux的背景介绍 Linux 操作系统的发展历程充满了激情与创新喵&#xff5e;&#x1f380; 萌芽期 (1983 - 1991)&#xff1a;Linux 的历史可追溯到 1983 年&#xff0c;理查德斯托曼 (Richard Stallman) 发起 GNU 计划&#xff0c;目标是创建一个自由软件操作系统。1987 年发…

Angular 和 Vue2.0 对比

前言 &#xff1a;“业精于勤&#xff0c;荒于嬉&#xff1b;行成于思&#xff0c;毁于随” 很久没写博客了&#xff0c;大多记录少进一步探查。 Angular 和 Vue2.0 对比&#xff1a; 一.概念 1.1 Angular 框架&#xff1a; 是一款由谷歌开发的开源web前端框架&#xff08;核…

【Python】轻松实现机器翻译:Transformers库使用教程

轻松实现机器翻译&#xff1a;Transformers库使用教程 近年来&#xff0c;机器翻译技术飞速发展&#xff0c;从传统的基于规则的翻译到统计机器翻译&#xff0c;再到如今流行的神经网络翻译模型&#xff0c;尤其是基于Transformer架构的模型&#xff0c;翻译效果已经有了质的飞…

[2024最新] macOS 发起 Bilibili 直播(不使用 OBS)

文章目录 1、B站账号 主播认证2、开启直播3、直播设置添加素材、隐私设置指定窗口添加/删除 窗口 4、其它说明官方直播帮助中心直播工具教程 目前搜到的 macOS 直播教程都比较古早&#xff0c;大部分都使用 OBS&#xff0c;一番探索下来&#xff0c;发现目前已经不需要 OBS了&a…

前端 性能优化 (图片与样式篇)

文章目录 前端能够做哪些图片优化?1、减小图片体积2、图片缓存服务工作线程 (Service Workers)缓存IndexDB缓存图片LocalStorage缓存 3、图片懒加载使用 loading"lazy" 属性 4、不同分辨率下使用不同的图片5、使用webp格式的图片6、配置图片CDN7、减少图片和动图的使…

【MySQL 保姆级教学】事务的自动提交和手动提交(重点)--上(13)

目录 1. 什么是事务&#xff1f;2. 事务的版本支持3. 事务提交的方式3.1 事务提交方式的分类3.2 演示的准备的工作3.2.1 创建表3.2.2 MySQL的服务端和客户端3.2.3 调低事务的隔离级别 4. 手动提交4.1 手动提交的命令说明4.2 示例一4.3 示例二4.4 示例三4.5 示例四 5. 自动提交5…

C++ | Leetcode C++题解之第546题移除盒子

题目&#xff1a; 题解&#xff1a; class Solution { public:int dp[100][100][100];int removeBoxes(vector<int>& boxes) {memset(dp, 0, sizeof dp);return calculatePoints(boxes, 0, boxes.size() - 1, 0);}int calculatePoints(vector<int>& boxes…

RK3588部署ppocr流程及安装环境_笔记1

前言&#xff1a; RK3588部署ppocr流程及ubuntu安装环境 目录 一、NoMachine安装使用 二、把ubuntu系统从英文修改为中文界面 三、安装conda 没有报错说明没有问题&#xff0c;如果source的时候报错&#xff0c;查看 ​编辑 报这种错&#xff1a; 5、需要添加国内镜像源…

二分查找习题篇(下)

二分查找习题篇(下) 1.山脉数组的峰顶索引 题目描述&#xff1a; 给定一个长度为 n 的整数 山脉 数组 arr &#xff0c;其中的值递增到一个 峰值元素 然后递减。 返回峰值元素的下标。 你必须设计并实现时间复杂度为 O(log(n)) 的解决方案。 示例 1&#xff1a; 输入&#xf…