Linux和windows进程同步与线程同步那些事儿(五):Linux下进程同步

Linux和windows进程同步与线程同步那些事儿(一)
Linux和windows进程同步与线程同步那些事儿(二): windows线程同步详解示例
Linux和windows进程同步与线程同步那些事儿(三): Linux线程同步详解示例
Linux和windows进程同步与线程同步那些事儿(四):windows 下进程同步
Linux和windows进程同步与线程同步那些事儿(五):Linux下进程同步

在Linux中,进程同步可以通过多种机制来实现,其中最常见的包括信号量(semaphore)、共享内存(shared memory)、管道(pipe)、消息队列(message queue)和文件锁(file lock)等。

1. 信号量(Semaphore):

信号量是一种经典的进程同步机制,它可以用于控制对共享资源的访问。
在Linux中,可以使用sem_t类型的信号量来实现进程同步。

在Linux下,可以使用信号量实现多进程之间的同步。信号量是一个计数器,用于多个进程之间共享资源的同步操作。

下面是一个使用信号量进行多处理器同步的C++代码示例:

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>// 定义信号量的键值
#define KEY 123456// 定义信号量的个数
#define NUM_SEMS 1union semun {int val;struct semid_ds *buf;unsigned short *array;
};int main() {// 创建信号量int semid = semget(KEY, NUM_SEMS, IPC_CREAT | 0666);if (semid == -1) {std::cerr << "Failed to create semaphore" << std::endl;return 1;}// 初始化信号量union semun arg;arg.val = 0;if (semctl(semid, 0, SETVAL, arg) == -1) {std::cerr << "Failed to initialize semaphore" << std::endl;return 1;}// 创建子进程pid_t pid = fork();if (pid == -1) {std::cerr << "Failed to fork process" << std::endl;return 1;} else if (pid == 0) {// 子进程和父进程通过信号量进行同步操作// P操作,等待信号量计数器大于0struct sembuf sop;sop.sem_num = 0;sop.sem_op = -1;sop.sem_flg = 0;if (semop(semid, &sop, 1) == -1) {std::cerr << "Failed to perform P operation" << std::endl;return 1;}// 输出一段文字std::cout << "Child process output" << std::endl;// V操作,增加信号量计数器sop.sem_op = 1;if (semop(semid, &sop, 1) == -1) {std::cerr << "Failed to perform V operation" << std::endl;return 1;}} else {// 父进程和子进程通过信号量进行同步操作// 输出一段文字std::cout << "Parent process output" << std::endl;// V操作,增加信号量计数器struct sembuf sop;sop.sem_num = 0;sop.sem_op = 1;sop.sem_flg = 0;if (semop(semid, &sop, 1) == -1) {std::cerr << "Failed to perform V operation" << std::endl;return 1;}// P操作,等待信号量计数器大于0sop.sem_op = -1;if (semop(semid, &sop, 1) == -1) {std::cerr << "Failed to perform P operation" << std::endl;return 1;}}// 删除信号量if (semctl(semid, 0, IPC_RMID) == -1) {std::cerr << "Failed to remove semaphore" << std::endl;return 1;}return 0;
}

在上面的代码示例中,首先使用semget函数创建了一个信号量集,通过指定键值和信号量的个数来创建。然后使用semctl函数初始化了信号量的计数器为0。接着通过fork函数创建了一个子进程。

在子进程中,使用semop函数进行P操作,即等待信号量计数器大于0;然后输出一段文字;再使用semop函数进行V操作,即增加信号量计数器。

在父进程中,首先输出一段文字;然后使用semop函数进行V操作;最后使用semop函数进行P操作。

这样,父进程和子进程通过信号量的P操作和V操作进行同步,保证了子进程的输出一定在父进程的输出之后。

最后,使用semctl函数删除了创建的信号量集。

运行代码示例,可以看到父进程先输出一段文字,然后子进程再输出一段文字,证明了通过信号量实现了多进程的同步。

需要注意的是,上述代码示例只使用了一个信号量来进行同步,如果需要更复杂的同步操作,可以使用多个信号量来实现。此外,需要保证在使用信号量之前先创建信号量,并在使用完毕后删除信号量。


2. 共享内存(Shared Memory):

共享内存允许多个进程访问同一块内存区域,从而实现进程间的数据共享和通信。
在Linux中,可以使用shmgetshmat等系统调用来创建和访问共享内存区域。

代码示例1:

在Linux下,使用共享内存进行多进程间的同步一般会用到信号量(semaphore)来进行进程间的互斥和同步操作。下面是一个使用C进行编写的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <unistd.h>// 定义信号量的操作结构体
union semun {int val;struct semid_ds *buf;unsigned short int *array;struct seminfo *__buf;
};// 定义共享内存的大小
#define SHM_SIZE 1024// 定义信号量操作函数
int sem_op(int semid, int sem_num, int op) {struct sembuf semop = {sem_num, op, SEM_UNDO};return semop(semid, &semop, 1);
}int main() {key_t key;int shmid, semid;char *shm, *s;// 创建key值key = ftok(".", 'S');if (key == -1) {perror("ftok error");exit(1);}// 创建共享内存shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);if (shmid == -1) {perror("shmget error");exit(1);}// 关联共享内存shm = shmat(shmid, NULL, 0);if (shm == (char *)-1) {perror("shmat error");exit(1);}// 创建信号量semid = semget(key, 1, IPC_CREAT | 0666);if (semid == -1) {perror("semget error");exit(1);}// 初始化信号量union semun sem_val;sem_val.val = 1;if (semctl(semid, 0, SETVAL, sem_val) == -1) {perror("semctl error");exit(1);}// 多进程同步pid_t pid = fork();if (pid == -1) {perror("fork error");exit(1);} else if (pid == 0) { // 子进程// P 操作if (sem_op(semid, 0, -1) == -1) {perror("sem_op error");exit(1);}// 操作共享内存s = shm;for (char c = 'a'; c <= 'z'; c++) {*s++ = c;usleep(10000);}// V 操作if (sem_op(semid, 0, 1) == -1) {perror("sem_op error");exit(1);}// 分离共享内存if (shmdt(shm) == -1) {perror("shmdt error");exit(1);}exit(0);} else { // 父进程// P 操作if (sem_op(semid, 0, -1) == -1) {perror("sem_op error");exit(1);}// 读取并输出共享内存s = shm;while (*s != '\0') {putchar(*s++);usleep(10000);}putchar('\n');// V 操作if (sem_op(semid, 0, 1) == -1) {perror("sem_op error");exit(1);}// 删除共享内存if (shmctl(shmid, IPC_RMID, NULL) == -1) {perror("shmctl error");exit(1);}// 删除信号量if (semctl(semid, IPC_RMID, 0) == -1) {perror("semctl error");exit(1);}exit(0);}return 0;
}

这段代码通过创建共享内存和信号量来实现多进程间的同步,其中子进程将字母逐个写入共享内存,父进程从共享内存中读取并输出字母。

首先,使用ftok函数创建一个唯一的key值,用于共享内存和信号量的标识。然后,使用shmget函数创建共享内存,指定大小为SHM_SIZE。接着,使用shmat函数关联共享内存并返回一个指向共享内存的指针。再然后,使用semget函数创建一个信号量,并使用semctl函数初始化信号量的值为1。

接下来,使用fork函数创建一个子进程。在子进程中,使用sem_op函数进行P操作(信号量减1),然后通过指针s操作共享内存,将字母逐个写入共享内存。最后,使用sem_op函数进行V操作(信号量加1),然后使用shmdt函数分离共享内存。

在父进程中,使用sem_op函数进行P操作,然后通过指针s从共享内存中读取并输出字母,直到遇到结束符’\0’。然后,使用sem_op函数进行V操作。最后,使用shmctl函数删除共享内存,使用semctl函数删除信号量。

代码示例2:

以下是一个使用共享内存实现多进程同步的示例代码,分为A和B两个程序:

程序A:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/wait.h>#define SHM_KEY 1234
#define SHM_SIZE 256int main() {// 创建共享内存int shmid = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT | 0666);if (shmid == -1) {perror("shmget");exit(1);}// 连接共享内存char* shared_memory = (char*)shmat(shmid, NULL, 0);if (shared_memory == (char*)-1) {perror("shmat");exit(1);}// 写入数据到共享内存sprintf(shared_memory, "Hello, World!");// 创建子进程pid_t pid = fork();if (pid == -1) {perror("fork");exit(1);} else if (pid == 0) {// 子进程等待一段时间sleep(1);// 父子进程间同步,等待父进程写入数据while (shared_memory[0] == '\0') {sleep(1);}// 读取并打印共享内存中的数据printf("Data in shared memory: %s\n", shared_memory);// 断开连接共享内存if (shmdt(shared_memory) == -1) {perror("shmdt");exit(1);}exit(0);} else {// 等待子进程结束wait(NULL);// 断开连接共享内存if (shmdt(shared_memory) == -1) {perror("shmdt");exit(1);}// 删除共享内存if (shmctl(shmid, IPC_RMID, NULL) == -1) {perror("shmctl");exit(1);}}return 0;
}

程序B:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>#define SHM_KEY 1234
#define SHM_SIZE 256int main() {// 获取共享内存的IDint shmid = shmget(SHM_KEY, SHM_SIZE, 0666);if (shmid == -1) {perror("shmget");exit(1);}// 连接共享内存char* shared_memory = (char*)shmat(shmid, NULL, 0);if (shared_memory == (char*)-1) {perror("shmat");exit(1);}// 等待父进程写入数据while (shared_memory[0] == '\0') {sleep(1);}// 读取并打印共享内存中的数据printf("Data in shared memory: %s\n", shared_memory);// 断开连接共享内存if (shmdt(shared_memory) == -1) {perror("shmdt");exit(1);}return 0;
}

以上代码展示了一个简单的多进程同步示例。程序A创建了共享内存并将数据写入其中,然后创建了一个子进程程序B。程序B连接到共享内存,等待程序A写入数据后读取并打印。程序A和程序B通过共享内存进行了数据的同步。

注意代码中的关键步骤:

  1. 使用shmget函数创建共享内存,指定一个唯一的键值和大小。
  2. 使用shmat函数连接共享内存,获取指向共享内存的指针。
  3. 子进程通过轮询等待共享内存中的数据,直到非空。
  4. 父进程和子进程完成后,使用shmdt函数断开与共享内存的连接。
  5. 父进程使用shmctl函数删除共享内存。

这样,两个进程就能够实现通过共享内存进行数据同步了。


3. 管道(Pipe):

管道是一种单向的通信机制,可以用于实现具有父子关系的进程间通信。
在Linux中,可以使用pipe系统调用来创建管道。
在Linux下,使用管道进行多进程同步一般会用到父子进程间的通信机制。下面是一个使用C进行编写的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>int main() {int fd[2];pid_t pid;char buf[100];// 创建管道if (pipe(fd) == -1) {perror("pipe error");exit(1);}// 创建子进程pid = fork();if (pid == -1) {perror("fork error");exit(1);} else if (pid == 0) { // 子进程close(fd[0]); // 关闭读端// 在子进程中向管道写入数据char *str = "Hello from child process!";write(fd[1], str, strlen(str) + 1);printf("Child process writes to pipe: %s\n", str);close(fd[1]); // 关闭写端exit(0);} else { // 父进程close(fd[1]); // 关闭写端// 在父进程中从管道读取数据read(fd[0], buf, sizeof(buf));printf("Parent process reads from pipe: %s\n", buf);close(fd[0]); // 关闭读端exit(0);}return 0;
}

这段代码通过创建管道来实现父子进程间的数据传输和同步,其中子进程向管道写入数据,父进程从管道读取数据并输出。

首先,使用pipe函数创建一个管道,返回的fd数组包含两个文件描述符,fd[0]用于读取数据,fd[1]用于写入数据。

接下来,使用fork函数创建一个子进程。在子进程中,关闭fd[0]读端,使用write函数向管道写入数据,然后关闭fd[1]写端。

在父进程中,关闭fd[1]写端,使用read函数从管道中读取数据,然后输出到屏幕上。最后,关闭fd[0]读端。

以上就是使用管道进行多进程同步的C代码示例及详细讲解。通过在父子进程间传输数据进行同步,父进程从管道中读取数据时会阻塞,直到子进程写入数据到管道中。这样就实现了简单的多进程同步。


4. 消息队列(Message Queue):

消息队列允许进程之间通过消息进行通信,可以实现进程间的异步通信。
在Linux中,可以使用msggetmsgsndmsgrcv等系统调用来创建和操作消息队列。

在Linux下,可以使用消息队列来实现多进程之间的同步。消息队列是一种进程间通信的方式,可以在不同进程之间传递数据。下面是一个示例,展示了如何使用消息队列实现多进程同步。

程序A:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>#define MSG_SIZE 128// 定义消息结构体
struct msg_buffer {long msg_type;char msg_text[MSG_SIZE];
};int main() {key_t key;int msg_id;struct msg_buffer message;// 生成唯一的keykey = ftok("msg_queue_example", 65);// 创建消息队列,如果已经存在则打开msg_id = msgget(key, 0666 | IPC_CREAT);if (msg_id == -1) {perror("msgget");return -1;}// 接收来自程序B的消息msgrcv(msg_id, &message, MSG_SIZE, 1, 0);printf("Received message: %s\n", message.msg_text);// 向程序B发送消息message.msg_type = 2;sprintf(message.msg_text, "Hello from A");msgsnd(msg_id, &message, sizeof(message), 0);printf("Message sent: %s\n", message.msg_text);// 删除消息队列msgctl(msg_id, IPC_RMID, NULL);return 0;
}

程序B:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>#define MSG_SIZE 128// 定义消息结构体
struct msg_buffer {long msg_type;char msg_text[MSG_SIZE];
};int main() {key_t key;int msg_id;struct msg_buffer message;// 生成唯一的keykey = ftok("msg_queue_example", 65);// 连接到消息队列msg_id = msgget(key, 0666 | IPC_CREAT);if (msg_id == -1) {perror("msgget");return -1;}// 向程序A发送消息message.msg_type = 1;sprintf(message.msg_text, "Hello from B");msgsnd(msg_id, &message, sizeof(message), 0);printf("Message sent: %s\n", message.msg_text);// 接收来自程序A的消息msgrcv(msg_id, &message, MSG_SIZE, 2, 0);printf("Received message: %s\n", message.msg_text);return 0;
}

在程序A中,首先调用ftok函数生成一个唯一的key,用于创建消息队列。然后使用msgget函数创建或打开一个消息队列,如果队列已经存在,则打开;如果队列不存在,则创建一个新的队列。然后使用msgrcv函数接收来自程序B的消息,并打印接收到的消息内容。接下来,向程序B发送一个消息,使用msgsnd函数。最后,使用msgctl函数删除消息队列。

在程序B中,也是首先调用ftok函数生成一个唯一的key,用于连接到消息队列。然后使用msgget函数连接到消息队列。接下来,向程序A发送一个消息,使用msgsnd函数。然后使用msgrcv函数接收来自程序A的消息,并打印接收到的消息内容。

这个示例展示了两个进程之间如何使用消息队列进行通信和同步。程序A和程序B分别使用不同的消息类型作为消息的标志,以便接收和发送不同类型的消息。msgrcvmsgsnd函数用于接收和发送消息。


5. 文件锁(File Lock):

文件锁可以用于控制对文件的访问,从而实现进程间的同步。
在Linux中,可以使用fcntl系统调用来对文件进行加锁和解锁操作。
在Linux下,可以使用文件锁(fcntl)来实现多进程之间的同步。文件锁可以用于进程间共享的文件,通过对文件进行加锁和解锁,可以实现进程之间的互斥访问。下面是一个示例,展示了如何使用文件锁实现多进程同步。

程序A:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main() {int fd;struct flock lock;// 打开共享文件fd = open("shared_file.txt", O_RDWR | O_CREAT, 0666);if (fd == -1) {perror("open");return -1;}// 加锁lock.l_type = F_WRLCK;   // 写锁lock.l_whence = SEEK_SET;lock.l_start = 0;lock.l_len = 0;          // 锁定整个文件if (fcntl(fd, F_SETLKW, &lock) == -1) {perror("fcntl");return -1;}printf("A: File locked\n");sleep(5); // 模拟处理时间// 解锁lock.l_type = F_UNLCK;if (fcntl(fd, F_SETLK, &lock) == -1) {perror("fcntl");return -1;}printf("A: File unlocked\n");return 0;
}

程序B:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main() {int fd;struct flock lock;// 打开共享文件fd = open("shared_file.txt", O_RDWR | O_CREAT, 0666);if (fd == -1) {perror("open");return -1;}// 加锁lock.l_type = F_WRLCK;   // 写锁lock.l_whence = SEEK_SET;lock.l_start = 0;lock.l_len = 0;          // 锁定整个文件if (fcntl(fd, F_SETLKW, &lock) == -1) {perror("fcntl");return -1;}printf("B: File locked\n");sleep(5); // 模拟处理时间// 解锁lock.l_type = F_UNLCK;if (fcntl(fd, F_SETLK, &lock) == -1) {perror("fcntl");return -1;}printf("B: File unlocked\n");return 0;
}

在程序A中,首先使用open函数打开共享文件,如果文件不存在则创建。然后定义一个struct flock结构体用于加锁和解锁。接下来,设置锁的类型为写锁,通过fcntl函数的F_SETLKW参数来加锁,并指定锁定的范围为整个文件。等待一段时间(这里用sleep(5)模拟处理时间)。最后,设置锁的类型为解锁,通过fcntl函数来解锁。

程序B的逻辑与程序A类似,首先使用open函数打开共享文件,然后设置锁的类型为写锁,通过fcntl函数的F_SETLKW参数来加锁,并指定锁定的范围为整个文件。等待一段时间,最后解锁。

这个示例展示了两个进程之间如何使用文件锁实现同步。进程A和进程B都对共享文件进行加锁,当一个进程已经持有锁时,另一个进程会在fcntl函数上阻塞,直到持有锁的进程释放锁。这样保证了进程A和进程B可以交替地对共享资源进行访问,实现了同步。

拓展讲解:

既然说到了文件锁,有必要在此强调下读锁、写锁和读写锁,对于不同的应用场景,合理选择文件锁,可以优化程序执行效率。
在Linux中,文件锁(fcntl)包括读锁和写锁两种类型,以及读写锁(pthread_rwlock)。

  1. 读锁(Shared Lock):

    • 多个进程可以同时持有读锁。
    • 读锁是共享的,多个进程可以同时读取文件。
    • 当有进程持有写锁时,请求读锁的进程会被阻塞,直到所有写锁都被释放。
  2. 写锁(Exclusive Lock):

    • 写锁是独占的,同一时间只能有一个进程持有写锁。
    • 写锁保证了独占的访问权限,其他进程无法读取或写入文件。
    • 当有进程持有读锁或写锁时,请求写锁的进程会被阻塞,直到所有读锁和写锁都被释放。
  3. 读写锁(Read-Write Lock):

    • 读写锁是一种更高级别的文件锁,提供了更细粒度的控制。
    • 读写锁允许多个进程同时持有读锁,但只允许一个进程持有写锁。
    • 当有进程持有写锁时,请求读锁或写锁的进程都会被阻塞,直到写锁被释放。

下面是一个使用文件锁和读写锁的示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>// 文件锁示例
void fileLockExample() {int fd;struct flock lock;// 打开文件fd = open("shared_file.txt", O_RDWR | O_CREAT, 0666);if (fd == -1) {perror("open");return;}// 加写锁lock.l_type = F_WRLCK;lock.l_whence = SEEK_SET;lock.l_start = 0;lock.l_len = 0;if (fcntl(fd, F_SETLKW, &lock) == -1) {perror("lock");return;}printf("File locked\n");sleep(5); // 模拟处理时间// 解锁lock.l_type = F_UNLCK;if (fcntl(fd, F_SETLK, &lock) == -1) {perror("unlock");return;}printf("File unlocked\n");close(fd);
}// 读写锁示例
pthread_rwlock_t rwlock;void* reader(void* arg) {pthread_rwlock_rdlock(&rwlock);printf("Reader: Reading shared resource\n");sleep(3); // 模拟读取时间pthread_rwlock_unlock(&rwlock);printf("Reader: Finished reading\n");return NULL;
}void* writer(void* arg) {pthread_rwlock_wrlock(&rwlock);printf("Writer: Writing to shared resource\n");sleep(3); // 模拟写入时间pthread_rwlock_unlock(&rwlock);printf("Writer: Finished writing\n");return NULL;
}void rwLockExample() {pthread_t readerThread1, readerThread2, writerThread;pthread_rwlock_init(&rwlock, NULL);// 创建读者线程pthread_create(&readerThread1, NULL, reader, NULL);pthread_create(&readerThread2, NULL, reader, NULL);// 创建写者线程pthread_create(&writerThread, NULL, writer, NULL);// 等待线程结束pthread_join(readerThread1, NULL);pthread_join(readerThread2, NULL);pthread_join(writerThread, NULL);pthread_rwlock_destroy(&rwlock);
}int main() {printf("File Lock Example:\n");fileLockExample();printf("\nRead-Write Lock Example:\n");rwLockExample();return 0;
}

在示例中,fileLockExample函数展示了如何使用fcntl函数实现文件锁。首先打开共享文件,在加锁之前设置锁的类型为写锁。然后,通过fcntl函数的F_SETLKW参数来加锁,使用F_UNLCK参数来解锁。

rwLockExample函数展示了如何使用读写锁(pthread_rwlock)实现多线程同步。首先初始化读写锁,然后创建读者线程和写者线程。读者线程使用pthread_rwlock_rdlock函数加读锁,写者线程使用pthread_rwlock_wrlock函数加写锁。最后,使用pthread_rwlock_unlock函数解锁,并销毁读写锁。

文件锁和读写锁总结:

  • 文件锁提供了对共享文件的互斥访问,读锁和写锁之间的关系是互斥的。
  • 读写锁提供了更高级别的文件访问控制,允许多个进程同时读取文件,但只允许一个进程写入文件。

几种进程间同步方式的优缺点比较

linux进程间同步可以使用信号量、共享内存、管道、消息队列和文件锁等机制。下面是它们的使用场景及优缺点的比较:

1. 信号量:

  • 使用场景:适用于进程间的互斥和同步操作,如控制临界区访问、资源的分配和释放等。
  • 优点:简单易用,适用于进程间的基本同步和互斥操作。
  • 缺点:需要手动编写代码来保证进程对信号量的正确使用,容易出错。不适用于跨网络的进程通信。

2. 共享内存:

  • 使用场景:适用于大量数据共享和频繁的数据交换,如图像、音频或视频数据的处理。
  • 优点:高效,进程可以直接访问共享内存区域,无需数据的拷贝。
  • 缺点:需要进行进程间的同步,以避免竞态条件和数据一致性问题。对内存管理要求较高,可能会导致内存泄漏或悬挂进程。

3. 管道:

  • 使用场景:适用于父子进程间的通信,管道只能在具有父子关系的进程之间使用。
  • 优点:简单易用,无需手动同步,通过文件描述符进行进程间通信。
  • 缺点:仅适用于具有父子关系的进程,只能实现单向通信。

4. 消息队列:

  • 使用场景:适用于不同进程间的异步通信,如进程间的命令、事件、消息传递等。
  • 优点:可以实现多对多的通信,不需要进程具有父子关系。具有高度的灵活性和可扩展性。
  • 缺点:数据的大小受限,对于大量数据的传输可能不太高效。

5. 文件锁:

  • 使用场景:适用于对文件进行互斥访问的场景,如进程间共享的文件或资源。
  • 优点:简单易用,可以保证共享文件的互斥访问。
  • 缺点:对文件操作需要显式加锁和解锁,繁琐且容易出错。不适用于跨网络的进程通信。

综上所述,不同的机制适用于不同的场景。选择适当的进程间同步机制取决于具体的需求和限制。信号量和共享内存适用于高性能数据共享和同步,管道适用于具有父子关系的进程通信,消息队列适用于异步通信,文件锁适用于文件的互斥访问。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux基础知识(文件类型、目录、文件权限、权限修改)

Linux基础知识&#xff08;文件类型、目录、文件权限、权限修改&#xff09; 文章目录 Linux基础知识&#xff08;文件类型、目录、文件权限、权限修改&#xff09;0x01 文件类型0x02 常用目录0x03 文件系统权限0x04 权限修改 0x01 文件类型 Linux中的文件分类主要基于其内容和…

基于ssm快餐店点餐结算系统的设计与实现+vue论文

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装快餐店点餐结算系统软件来发挥其高效地信息处理的作用&…

前端计算精度丢失问题

// 精度丢失异常数据汇总 10.3950 * 3935.00 期望40904.33&#xff0c; 异常结果40904.32 7.3950 * 3835.00 期望28359.83&#xff0c; 异常结果28359.82 11.777 * 4215 期望49640.06&#xff0c; 异常结果49640.05 12.445 * 4005 期望49842.23&#xff0c;异常结果49842.2…

区间预测 | Matlab实现CNN-LSTM-KDE的卷积长短期神经网络结合核密度估计多变量时序区间预测

区间预测 | Matlab实现CNN-LSTM-KDE的卷积长短期神经网络结合核密度估计多变量时序区间预测 目录 区间预测 | Matlab实现CNN-LSTM-KDE的卷积长短期神经网络结合核密度估计多变量时序区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.CNN-LSTM-KDE多变量时间序列区…

使用 Windbg 分析软件异常时的诸多细节与技巧总结

目录 1、dump文件 1.1、dump文件的生成方式 1.2、dump文件的大小 2、pdb符号文件 2.1、pdb文件的路径设置 2.2、pdb文件的时间戳与名称问题 2.3、如何确定要找哪些pdb文件&#xff1f; 3、使用Windbg静态分析dump文件以及动态调试程序的一般步骤 4、确定发生异常或崩溃…

能力素质模型在企业中的应用

在我国经济飞速发展中&#xff0c;企业也获得了越来越多的业务。业务的增加带来的是人力管理上的不足&#xff0c;很多企业一味的着眼于业务的营收&#xff0c;而忽视了人力资源管理上的跟进&#xff0c;人力资源管理水平逐渐跟不上业务的迅猛发展&#xff0c;所以企业需要一套…

程序员的海外营收

在素材图片平台生成AI图片&#xff0c;通过下载赚取版税。 Wirestock这个平台能让你方便地把作品上传到多个素材市场&#xff0c;比如Adobe Stock、Shutterstock和iStock等。 你只需要上传一次&#xff0c;就能在多个平台上销售。 你可以开作坊、网上教程&#xff0c;或者在…

使用Netty实现Socket网络编程

** Netty初步讲解和认识 ** 网络通信模型 Netty支持多种网络通信模型&#xff0c;包括传统的阻塞I/O、非阻塞I/O、多路复用I/O和异步I/O。其中&#xff0c;非阻塞I/O和多路复用I/O是Netty的核心特性。 非阻塞I/O&#xff1a;Netty通过使用Java的NIO&#xff08;New I/O&…

JavaScript删除数组中指定元素的五种方法有哪些

JavaScript中删除数组中指定元素的方法有多种&#xff0c;以下列出五种常见方法&#xff1a; 使用filter()方法&#xff1a;filter()方法创建一个新数组&#xff0c;新数组中的元素是通过检查指定条件后从原数组中筛选出来的。如果元素满足条件&#xff0c;它就会被包含在新数组…

buuctf[极客大挑战 2019]BabySQL--联合注入、双写过滤

目录 1、测试万能密码&#xff1a; 2、判断字段个数 3、尝试联合注入 4、尝试双写过滤 5、继续尝试列数 6、查询数据库和版本信息 7、查询表名 8、没有找到和ctf相关的内容&#xff0c;查找其他的数据库 9、查看ctf数据库中的表 10、查询Flag表中的字段名 11、查询表…

Python图像处理【17】指纹增强和细节提取

指纹增强和细节提取 0. 前言1. 形态学操作基础2. 利用形态学操作进行指纹增强3. 从增强指纹中提取特征(细节)3.1 指纹细节概念3.2 提取指纹细节 小结系列链接 0. 前言 指纹识别和验证是最古老&#xff0c;最流行和广泛使用的生物特征技术。众所周知&#xff0c;每个人都有独特…

模型\视图一般步骤:为什么经常要用“选择模型”QItemSelectionModel?

一、“使用视图”一般的步骤&#xff1a; //1.创建 模型(这里是数据模型&#xff01;) tabModelnew QSqlTableModel(this,DB);//数据表 //2.设置 视图的模型(这里是数据模型&#xff01;) ui->tableView->setModel(tabModel); 模型种类&#xff1a; QStringListModel…

SemiDrive E3 打包说明

一、 概述 本文介绍 E3 PAC 打包&#xff0c;编译器生成 bin 文件需要通过打包生成 PAC 包&#xff0c;再通过 SDToolBox 工具将 PAC 包烧写到芯片&#xff0c;PAC 包的物理载体分为 Flash、eMMC、SD&#xff0c;一个 PAC包最多支持 3 个BootPackage&#xff1b;本文主要描述打…

代码随想录算法训练营第一天| 27 移除元素 704 二分查找

目录 27 移除元素 704 二分查找 27 移除元素 快指针遍历&#xff0c;慢指针记录 class Solution { public:int removeElement(vector<int>& nums, int val) {int l 0,r 0;for(;r < nums.size();r){if(nums[r] val){}else{nums[l] nums[r];}}return l;} }; …

iOS 应用上架指南:资料填写及提交审核

摘要 本文提供了iOS新站上架资料填写及提交审核的详细指南&#xff0c;包括创建应用、资料填写-综合、资料填写-IOS App和提交审核等步骤。通过本指南&#xff0c;您将了解到如何填写正确的资料&#xff0c;并顺利通过苹果公司的审核。 引言 在开发iOS应用后&#xff0c;将其…

医院里的管家婆,如何才能把科室的设备设施管理好?

在医院的各个科室中&#xff0c;有一位不可或缺的重要角色&#xff0c;科室的“管家婆”&#xff0c;大事、小事&#xff0c;事事都归她管&#xff0c;她就是护士长。她不仅是护理工作的核心&#xff0c;更是科室设备设施的“管家婆”。管理众多设备和资产&#xff0c;确保其正…

ArcMap实现多行标注

地图标注是地图的重要组成部分&#xff0c;也是地理信息的重要表达方式​。ArcMap的符号化系统为我们添加地图标注提供了方便&#xff0c;但是有时我们却需要添加多行标注&#xff0c;今天我们一起来探索一下ArcMap中两行标注的实现方式​。 首先&#xff0c;我们右击目标图层…

Oracle-探究统计信息收集自动采样AUTO_SAMPLE_SIZE

前言&#xff1a; Oracle数据库进行统计信息收集时&#xff0c;可以通过ESTIMATE_PERCENT参数指定采样方式或者比例&#xff0c;有以下4种指定的方式 1 统计信息收集时不指定值&#xff0c;这时候ESTIMATE_PERCENT值为默认值DBMS_STATS.AUTO_SAMPLE_SIZE&#xff0c;自动采样 …

TXB2 ELISA kit—Enzo Life Sciences ELISA试剂盒

高灵敏、经过充分验证的ELISA试剂盒&#xff0c;3小时内即可得结果 血栓素A2&#xff08;TXA2&#xff09;参与血小板聚集、血管收缩和生殖功能&#xff0c;但在生理条件下的半衰期仅有37秒。血栓素B2&#xff08;TXB2&#xff09;是TXA2非酶水合的稳定产物&#xff0c;因此体内…

Kafka之集群搭建

1. 为什么要使用kafka集群 单机服务下&#xff0c;Kafka已经具备了非常高的性能。TPS能够达到百万级别。但是&#xff0c;在实际工作中使用时&#xff0c;单机搭建的Kafka会有很大的局限性。 ​ 消息太多&#xff0c;需要分开保存。Kafka是面向海量消息设计的&#xff0c;一个T…