Linux和windows進(jìn)程同步與線程同步那些事兒(一)
Linux和windows進(jìn)程同步與線程同步那些事兒(二): windows線程同步詳解示例
Linux和windows進(jìn)程同步與線程同步那些事兒(三): Linux線程同步詳解示例
Linux和windows進(jìn)程同步與線程同步那些事兒(四):windows 下進(jìn)程同步
Linux和windows進(jìn)程同步與線程同步那些事兒(五):Linux下進(jìn)程同步
在Linux中,進(jìn)程同步可以通過多種機(jī)制來實(shí)現(xiàn),其中最常見的包括信號量(semaphore)、共享內(nèi)存(shared memory)、管道(pipe)、消息隊列(message queue)和文件鎖(file lock)等。
1. 信號量(Semaphore):
信號量是一種經(jīng)典的進(jìn)程同步機(jī)制,它可以用于控制對共享資源的訪問。
在Linux中,可以使用sem_t
類型的信號量來實(shí)現(xiàn)進(jìn)程同步。
在Linux下,可以使用信號量實(shí)現(xiàn)多進(jìn)程之間的同步。信號量是一個計數(shù)器,用于多個進(jìn)程之間共享資源的同步操作。
下面是一個使用信號量進(jìn)行多處理器同步的C++代碼示例:
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
// 定義信號量的鍵值
#define KEY 123456
// 定義信號量的個數(shù)
#define NUM_SEMS 1
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
int main() {
// 創(chuàng)建信號量
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;
}
// 創(chuàng)建子進(jìn)程
pid_t pid = fork();
if (pid == -1) {
std::cerr << "Failed to fork process" << std::endl;
return 1;
} else if (pid == 0) {
// 子進(jìn)程和父進(jìn)程通過信號量進(jìn)行同步操作
// P操作,等待信號量計數(shù)器大于0
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 P operation" << std::endl;
return 1;
}
// 輸出一段文字
std::cout << "Child process output" << std::endl;
// V操作,增加信號量計數(shù)器
sop.sem_op = 1;
if (semop(semid, &sop, 1) == -1) {
std::cerr << "Failed to perform V operation" << std::endl;
return 1;
}
} else {
// 父進(jìn)程和子進(jìn)程通過信號量進(jìn)行同步操作
// 輸出一段文字
std::cout << "Parent process output" << std::endl;
// V操作,增加信號量計數(shù)器
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操作,等待信號量計數(shù)器大于0
sop.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
函數(shù)創(chuàng)建了一個信號量集,通過指定鍵值和信號量的個數(shù)來創(chuàng)建。然后使用semctl
函數(shù)初始化了信號量的計數(shù)器為0。接著通過fork
函數(shù)創(chuàng)建了一個子進(jìn)程。
在子進(jìn)程中,使用semop
函數(shù)進(jìn)行P操作,即等待信號量計數(shù)器大于0;然后輸出一段文字;再使用semop
函數(shù)進(jìn)行V操作,即增加信號量計數(shù)器。
在父進(jìn)程中,首先輸出一段文字;然后使用semop
函數(shù)進(jìn)行V操作;最后使用semop
函數(shù)進(jìn)行P操作。
這樣,父進(jìn)程和子進(jìn)程通過信號量的P操作和V操作進(jìn)行同步,保證了子進(jìn)程的輸出一定在父進(jìn)程的輸出之后。
最后,使用semctl
函數(shù)刪除了創(chuàng)建的信號量集。
運(yùn)行代碼示例,可以看到父進(jìn)程先輸出一段文字,然后子進(jìn)程再輸出一段文字,證明了通過信號量實(shí)現(xiàn)了多進(jìn)程的同步。
需要注意的是,上述代碼示例只使用了一個信號量來進(jìn)行同步,如果需要更復(fù)雜的同步操作,可以使用多個信號量來實(shí)現(xiàn)。此外,需要保證在使用信號量之前先創(chuàng)建信號量,并在使用完畢后刪除信號量。
2. 共享內(nèi)存(Shared Memory):
共享內(nèi)存允許多個進(jìn)程訪問同一塊內(nèi)存區(qū)域,從而實(shí)現(xiàn)進(jìn)程間的數(shù)據(jù)共享和通信。
在Linux中,可以使用shmget
和shmat
等系統(tǒng)調(diào)用來創(chuàng)建和訪問共享內(nèi)存區(qū)域。
代碼示例1:
在Linux下,使用共享內(nèi)存進(jìn)行多進(jìn)程間的同步一般會用到信號量(semaphore
)來進(jìn)行進(jìn)程間的互斥和同步操作。下面是一個使用C進(jìn)行編寫的示例代碼:
#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>
// 定義信號量的操作結(jié)構(gòu)體
union semun {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
// 定義共享內(nèi)存的大小
#define SHM_SIZE 1024
// 定義信號量操作函數(shù)
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;
// 創(chuàng)建key值
key = ftok(".", 'S');
if (key == -1) {
perror("ftok error");
exit(1);
}
// 創(chuàng)建共享內(nèi)存
shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget error");
exit(1);
}
// 關(guān)聯(lián)共享內(nèi)存
shm = shmat(shmid, NULL, 0);
if (shm == (char *)-1) {
perror("shmat error");
exit(1);
}
// 創(chuàng)建信號量
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);
}
// 多進(jìn)程同步
pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(1);
} else if (pid == 0) { // 子進(jìn)程
// P 操作
if (sem_op(semid, 0, -1) == -1) {
perror("sem_op error");
exit(1);
}
// 操作共享內(nèi)存
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);
}
// 分離共享內(nèi)存
if (shmdt(shm) == -1) {
perror("shmdt error");
exit(1);
}
exit(0);
} else { // 父進(jìn)程
// P 操作
if (sem_op(semid, 0, -1) == -1) {
perror("sem_op error");
exit(1);
}
// 讀取并輸出共享內(nèi)存
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);
}
// 刪除共享內(nèi)存
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;
}
這段代碼通過創(chuàng)建共享內(nèi)存和信號量來實(shí)現(xiàn)多進(jìn)程間的同步,其中子進(jìn)程將字母逐個寫入共享內(nèi)存,父進(jìn)程從共享內(nèi)存中讀取并輸出字母。
首先,使用ftok
函數(shù)創(chuàng)建一個唯一的key值,用于共享內(nèi)存和信號量的標(biāo)識。然后,使用shmget
函數(shù)創(chuàng)建共享內(nèi)存,指定大小為SHM_SIZE
。接著,使用shmat
函數(shù)關(guān)聯(lián)共享內(nèi)存并返回一個指向共享內(nèi)存的指針。再然后,使用semget
函數(shù)創(chuàng)建一個信號量,并使用semctl
函數(shù)初始化信號量的值為1。
接下來,使用fork
函數(shù)創(chuàng)建一個子進(jìn)程。在子進(jìn)程中,使用sem_op
函數(shù)進(jìn)行P操作(信號量減1),然后通過指針s
操作共享內(nèi)存,將字母逐個寫入共享內(nèi)存。最后,使用sem_op
函數(shù)進(jìn)行V操作(信號量加1),然后使用shmdt
函數(shù)分離共享內(nèi)存。
在父進(jìn)程中,使用sem_op
函數(shù)進(jìn)行P操作,然后通過指針s
從共享內(nèi)存中讀取并輸出字母,直到遇到結(jié)束符’\0’。然后,使用sem_op
函數(shù)進(jìn)行V操作。最后,使用shmctl
函數(shù)刪除共享內(nèi)存,使用semctl
函數(shù)刪除信號量。
代碼示例2:
以下是一個使用共享內(nèi)存實(shí)現(xiàn)多進(jìn)程同步的示例代碼,分為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 256
int main() {
// 創(chuàng)建共享內(nèi)存
int shmid = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget");
exit(1);
}
// 連接共享內(nèi)存
char* shared_memory = (char*)shmat(shmid, NULL, 0);
if (shared_memory == (char*)-1) {
perror("shmat");
exit(1);
}
// 寫入數(shù)據(jù)到共享內(nèi)存
sprintf(shared_memory, "Hello, World!");
// 創(chuàng)建子進(jìn)程
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
} else if (pid == 0) {
// 子進(jìn)程等待一段時間
sleep(1);
// 父子進(jìn)程間同步,等待父進(jìn)程寫入數(shù)據(jù)
while (shared_memory[0] == '\0') {
sleep(1);
}
// 讀取并打印共享內(nèi)存中的數(shù)據(jù)
printf("Data in shared memory: %s\n", shared_memory);
// 斷開連接共享內(nèi)存
if (shmdt(shared_memory) == -1) {
perror("shmdt");
exit(1);
}
exit(0);
} else {
// 等待子進(jìn)程結(jié)束
wait(NULL);
// 斷開連接共享內(nèi)存
if (shmdt(shared_memory) == -1) {
perror("shmdt");
exit(1);
}
// 刪除共享內(nèi)存
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 256
int main() {
// 獲取共享內(nèi)存的ID
int shmid = shmget(SHM_KEY, SHM_SIZE, 0666);
if (shmid == -1) {
perror("shmget");
exit(1);
}
// 連接共享內(nèi)存
char* shared_memory = (char*)shmat(shmid, NULL, 0);
if (shared_memory == (char*)-1) {
perror("shmat");
exit(1);
}
// 等待父進(jìn)程寫入數(shù)據(jù)
while (shared_memory[0] == '\0') {
sleep(1);
}
// 讀取并打印共享內(nèi)存中的數(shù)據(jù)
printf("Data in shared memory: %s\n", shared_memory);
// 斷開連接共享內(nèi)存
if (shmdt(shared_memory) == -1) {
perror("shmdt");
exit(1);
}
return 0;
}
以上代碼展示了一個簡單的多進(jìn)程同步示例。程序A創(chuàng)建了共享內(nèi)存并將數(shù)據(jù)寫入其中,然后創(chuàng)建了一個子進(jìn)程程序B。程序B連接到共享內(nèi)存,等待程序A寫入數(shù)據(jù)后讀取并打印。程序A和程序B通過共享內(nèi)存進(jìn)行了數(shù)據(jù)的同步。
注意代碼中的關(guān)鍵步驟:
- 使用
shmget
函數(shù)創(chuàng)建共享內(nèi)存,指定一個唯一的鍵值和大小。 - 使用
shmat
函數(shù)連接共享內(nèi)存,獲取指向共享內(nèi)存的指針。 - 子進(jìn)程通過輪詢等待共享內(nèi)存中的數(shù)據(jù),直到非空。
- 父進(jìn)程和子進(jìn)程完成后,使用
shmdt
函數(shù)斷開與共享內(nèi)存的連接。 - 父進(jìn)程使用
shmctl
函數(shù)刪除共享內(nèi)存。
這樣,兩個進(jìn)程就能夠?qū)崿F(xiàn)通過共享內(nèi)存進(jìn)行數(shù)據(jù)同步了。
3. 管道(Pipe):
管道是一種單向的通信機(jī)制,可以用于實(shí)現(xiàn)具有父子關(guān)系的進(jìn)程間通信。
在Linux中,可以使用pipe系統(tǒng)調(diào)用來創(chuàng)建管道。
在Linux下,使用管道進(jìn)行多進(jìn)程同步一般會用到父子進(jìn)程間的通信機(jī)制。下面是一個使用C進(jìn)行編寫的示例代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
int fd[2];
pid_t pid;
char buf[100];
// 創(chuàng)建管道
if (pipe(fd) == -1) {
perror("pipe error");
exit(1);
}
// 創(chuàng)建子進(jìn)程
pid = fork();
if (pid == -1) {
perror("fork error");
exit(1);
} else if (pid == 0) { // 子進(jìn)程
close(fd[0]); // 關(guān)閉讀端
// 在子進(jìn)程中向管道寫入數(shù)據(jù)
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]); // 關(guān)閉寫端
exit(0);
} else { // 父進(jìn)程
close(fd[1]); // 關(guān)閉寫端
// 在父進(jìn)程中從管道讀取數(shù)據(jù)
read(fd[0], buf, sizeof(buf));
printf("Parent process reads from pipe: %s\n", buf);
close(fd[0]); // 關(guān)閉讀端
exit(0);
}
return 0;
}
這段代碼通過創(chuàng)建管道來實(shí)現(xiàn)父子進(jìn)程間的數(shù)據(jù)傳輸和同步,其中子進(jìn)程向管道寫入數(shù)據(jù),父進(jìn)程從管道讀取數(shù)據(jù)并輸出。
首先,使用pipe
函數(shù)創(chuàng)建一個管道,返回的fd
數(shù)組包含兩個文件描述符,fd[0]
用于讀取數(shù)據(jù),fd[1]
用于寫入數(shù)據(jù)。
接下來,使用fork
函數(shù)創(chuàng)建一個子進(jìn)程。在子進(jìn)程中,關(guān)閉fd[0]
讀端,使用write
函數(shù)向管道寫入數(shù)據(jù),然后關(guān)閉fd[1]
寫端。
在父進(jìn)程中,關(guān)閉fd[1]
寫端,使用read
函數(shù)從管道中讀取數(shù)據(jù),然后輸出到屏幕上。最后,關(guān)閉fd[0]
讀端。
以上就是使用管道進(jìn)行多進(jìn)程同步的C代碼示例及詳細(xì)講解。通過在父子進(jìn)程間傳輸數(shù)據(jù)進(jìn)行同步,父進(jìn)程從管道中讀取數(shù)據(jù)時會阻塞,直到子進(jìn)程寫入數(shù)據(jù)到管道中。這樣就實(shí)現(xiàn)了簡單的多進(jìn)程同步。
4. 消息隊列(Message Queue):
消息隊列允許進(jìn)程之間通過消息進(jìn)行通信,可以實(shí)現(xiàn)進(jìn)程間的異步通信。
在Linux中,可以使用msgget
、msgsnd
和msgrcv
等系統(tǒng)調(diào)用來創(chuàng)建和操作消息隊列。
在Linux下,可以使用消息隊列來實(shí)現(xiàn)多進(jìn)程之間的同步。消息隊列是一種進(jìn)程間通信的方式,可以在不同進(jìn)程之間傳遞數(shù)據(jù)。下面是一個示例,展示了如何使用消息隊列實(shí)現(xiàn)多進(jìn)程同步。
程序A:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSG_SIZE 128
// 定義消息結(jié)構(gòu)體
struct msg_buffer {
long msg_type;
char msg_text[MSG_SIZE];
};
int main() {
key_t key;
int msg_id;
struct msg_buffer message;
// 生成唯一的key
key = ftok("msg_queue_example", 65);
// 創(chuàng)建消息隊列,如果已經(jīng)存在則打開
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發(fā)送消息
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
// 定義消息結(jié)構(gòu)體
struct msg_buffer {
long msg_type;
char msg_text[MSG_SIZE];
};
int main() {
key_t key;
int msg_id;
struct msg_buffer message;
// 生成唯一的key
key = ftok("msg_queue_example", 65);
// 連接到消息隊列
msg_id = msgget(key, 0666 | IPC_CREAT);
if (msg_id == -1) {
perror("msgget");
return -1;
}
// 向程序A發(fā)送消息
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中,首先調(diào)用ftok
函數(shù)生成一個唯一的key,用于創(chuàng)建消息隊列。然后使用msgget
函數(shù)創(chuàng)建或打開一個消息隊列,如果隊列已經(jīng)存在,則打開;如果隊列不存在,則創(chuàng)建一個新的隊列。然后使用msgrcv
函數(shù)接收來自程序B的消息,并打印接收到的消息內(nèi)容。接下來,向程序B發(fā)送一個消息,使用msgsnd
函數(shù)。最后,使用msgctl
函數(shù)刪除消息隊列。
在程序B中,也是首先調(diào)用ftok
函數(shù)生成一個唯一的key,用于連接到消息隊列。然后使用msgget
函數(shù)連接到消息隊列。接下來,向程序A發(fā)送一個消息,使用msgsnd
函數(shù)。然后使用msgrcv
函數(shù)接收來自程序A的消息,并打印接收到的消息內(nèi)容。
這個示例展示了兩個進(jìn)程之間如何使用消息隊列進(jìn)行通信和同步。程序A和程序B分別使用不同的消息類型作為消息的標(biāo)志,以便接收和發(fā)送不同類型的消息。msgrcv
和msgsnd
函數(shù)用于接收和發(fā)送消息。
5. 文件鎖(File Lock):
文件鎖可以用于控制對文件的訪問,從而實(shí)現(xiàn)進(jìn)程間的同步。
在Linux中,可以使用fcntl系統(tǒng)調(diào)用來對文件進(jìn)行加鎖和解鎖操作。
在Linux下,可以使用文件鎖(fcntl)來實(shí)現(xiàn)多進(jìn)程之間的同步。文件鎖可以用于進(jìn)程間共享的文件,通過對文件進(jìn)行加鎖和解鎖,可以實(shí)現(xiàn)進(jìn)程之間的互斥訪問。下面是一個示例,展示了如何使用文件鎖實(shí)現(xiàn)多進(jìn)程同步。
程序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
函數(shù)打開共享文件,如果文件不存在則創(chuàng)建。然后定義一個struct flock
結(jié)構(gòu)體用于加鎖和解鎖。接下來,設(shè)置鎖的類型為寫鎖,通過fcntl
函數(shù)的F_SETLKW
參數(shù)來加鎖,并指定鎖定的范圍為整個文件。等待一段時間(這里用sleep(5)
模擬處理時間)。最后,設(shè)置鎖的類型為解鎖,通過fcntl
函數(shù)來解鎖。
程序B的邏輯與程序A類似,首先使用open
函數(shù)打開共享文件,然后設(shè)置鎖的類型為寫鎖,通過fcntl
函數(shù)的F_SETLKW
參數(shù)來加鎖,并指定鎖定的范圍為整個文件。等待一段時間,最后解鎖。
這個示例展示了兩個進(jìn)程之間如何使用文件鎖實(shí)現(xiàn)同步。進(jìn)程A和進(jìn)程B都對共享文件進(jìn)行加鎖,當(dāng)一個進(jìn)程已經(jīng)持有鎖時,另一個進(jìn)程會在fcntl
函數(shù)上阻塞,直到持有鎖的進(jìn)程釋放鎖。這樣保證了進(jìn)程A和進(jìn)程B可以交替地對共享資源進(jìn)行訪問,實(shí)現(xiàn)了同步。
拓展講解:
既然說到了文件鎖,有必要在此強(qiáng)調(diào)下讀鎖、寫鎖和讀寫鎖,對于不同的應(yīng)用場景,合理選擇文件鎖,可以優(yōu)化程序執(zhí)行效率。
在Linux中,文件鎖(fcntl)包括讀鎖和寫鎖兩種類型,以及讀寫鎖(pthread_rwlock)。
-
讀鎖(Shared Lock):
- 多個進(jìn)程可以同時持有讀鎖。
- 讀鎖是共享的,多個進(jìn)程可以同時讀取文件。
- 當(dāng)有進(jìn)程持有寫鎖時,請求讀鎖的進(jìn)程會被阻塞,直到所有寫鎖都被釋放。
-
寫鎖(Exclusive Lock):
- 寫鎖是獨(dú)占的,同一時間只能有一個進(jìn)程持有寫鎖。
- 寫鎖保證了獨(dú)占的訪問權(quán)限,其他進(jìn)程無法讀取或?qū)懭胛募?/li>
- 當(dāng)有進(jìn)程持有讀鎖或?qū)戞i時,請求寫鎖的進(jìn)程會被阻塞,直到所有讀鎖和寫鎖都被釋放。
-
讀寫鎖(Read-Write Lock):
- 讀寫鎖是一種更高級別的文件鎖,提供了更細(xì)粒度的控制。
- 讀寫鎖允許多個進(jìn)程同時持有讀鎖,但只允許一個進(jìn)程持有寫鎖。
- 當(dāng)有進(jìn)程持有寫鎖時,請求讀鎖或?qū)戞i的進(jìn)程都會被阻塞,直到寫鎖被釋放。
下面是一個使用文件鎖和讀寫鎖的示例:
#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);
// 創(chuàng)建讀者線程
pthread_create(&readerThread1, NULL, reader, NULL);
pthread_create(&readerThread2, NULL, reader, NULL);
// 創(chuàng)建寫者線程
pthread_create(&writerThread, NULL, writer, NULL);
// 等待線程結(jié)束
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
函數(shù)展示了如何使用fcntl
函數(shù)實(shí)現(xiàn)文件鎖。首先打開共享文件,在加鎖之前設(shè)置鎖的類型為寫鎖。然后,通過fcntl
函數(shù)的F_SETLKW
參數(shù)來加鎖,使用F_UNLCK
參數(shù)來解鎖。
rwLockExample
函數(shù)展示了如何使用讀寫鎖(pthread_rwlock
)實(shí)現(xiàn)多線程同步。首先初始化讀寫鎖,然后創(chuàng)建讀者線程和寫者線程。讀者線程使用pthread_rwlock_rdlock
函數(shù)加讀鎖,寫者線程使用pthread_rwlock_wrlock
函數(shù)加寫鎖。最后,使用pthread_rwlock_unlock
函數(shù)解鎖,并銷毀讀寫鎖。
文件鎖和讀寫鎖總結(jié):
- 文件鎖提供了對共享文件的互斥訪問,讀鎖和寫鎖之間的關(guān)系是互斥的。
- 讀寫鎖提供了更高級別的文件訪問控制,允許多個進(jìn)程同時讀取文件,但只允許一個進(jìn)程寫入文件。
幾種進(jìn)程間同步方式的優(yōu)缺點(diǎn)比較
linux進(jìn)程間同步可以使用信號量、共享內(nèi)存、管道、消息隊列和文件鎖等機(jī)制。下面是它們的使用場景及優(yōu)缺點(diǎn)的比較:
1. 信號量:
- 使用場景:適用于進(jìn)程間的互斥和同步操作,如控制臨界區(qū)訪問、資源的分配和釋放等。
- 優(yōu)點(diǎn):簡單易用,適用于進(jìn)程間的基本同步和互斥操作。
- 缺點(diǎn):需要手動編寫代碼來保證進(jìn)程對信號量的正確使用,容易出錯。不適用于跨網(wǎng)絡(luò)的進(jìn)程通信。
2. 共享內(nèi)存:
- 使用場景:適用于大量數(shù)據(jù)共享和頻繁的數(shù)據(jù)交換,如圖像、音頻或視頻數(shù)據(jù)的處理。
- 優(yōu)點(diǎn):高效,進(jìn)程可以直接訪問共享內(nèi)存區(qū)域,無需數(shù)據(jù)的拷貝。
- 缺點(diǎn):需要進(jìn)行進(jìn)程間的同步,以避免競態(tài)條件和數(shù)據(jù)一致性問題。對內(nèi)存管理要求較高,可能會導(dǎo)致內(nèi)存泄漏或懸掛進(jìn)程。
3. 管道:
- 使用場景:適用于父子進(jìn)程間的通信,管道只能在具有父子關(guān)系的進(jìn)程之間使用。
- 優(yōu)點(diǎn):簡單易用,無需手動同步,通過文件描述符進(jìn)行進(jìn)程間通信。
- 缺點(diǎn):僅適用于具有父子關(guān)系的進(jìn)程,只能實(shí)現(xiàn)單向通信。
4. 消息隊列:
- 使用場景:適用于不同進(jìn)程間的異步通信,如進(jìn)程間的命令、事件、消息傳遞等。
- 優(yōu)點(diǎn):可以實(shí)現(xiàn)多對多的通信,不需要進(jìn)程具有父子關(guān)系。具有高度的靈活性和可擴(kuò)展性。
- 缺點(diǎn):數(shù)據(jù)的大小受限,對于大量數(shù)據(jù)的傳輸可能不太高效。
5. 文件鎖:文章來源:http://www.zghlxwxcb.cn/news/detail-788401.html
- 使用場景:適用于對文件進(jìn)行互斥訪問的場景,如進(jìn)程間共享的文件或資源。
- 優(yōu)點(diǎn):簡單易用,可以保證共享文件的互斥訪問。
- 缺點(diǎn):對文件操作需要顯式加鎖和解鎖,繁瑣且容易出錯。不適用于跨網(wǎng)絡(luò)的進(jìn)程通信。
綜上所述,不同的機(jī)制適用于不同的場景。選擇適當(dāng)?shù)倪M(jìn)程間同步機(jī)制取決于具體的需求和限制。信號量和共享內(nèi)存適用于高性能數(shù)據(jù)共享和同步,管道適用于具有父子關(guān)系的進(jìn)程通信,消息隊列適用于異步通信,文件鎖適用于文件的互斥訪問。文章來源地址http://www.zghlxwxcb.cn/news/detail-788401.html
到了這里,關(guān)于Linux和windows進(jìn)程同步與線程同步那些事兒(五):Linux下進(jìn)程同步的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!