pthread
pthread_mutex
pthread_mutex是一种基于线程的锁机制,用于保护共享资源,防止多个线程同时访问这些资源的同时进行修改。pthread_mutex的底层原理如下:
-
互斥锁:pthread_mutex是一种互斥锁(Mutex),多个线程不能同时占有同一把锁。线程需要等待锁被释放后才能获得锁,并进入临界区。
-
阻塞和解除阻塞:当锁被占用时,其他线程需要等待锁的释放。线程可以调用pthread_mutex_lock函数来请求锁。如果锁被占用,则线程会进入阻塞状态,直到锁被释放为止。而当锁的持有者调用pthread_mutex_unlock释放锁的时候,等待锁的线程会被解除阻塞,其中一个线程会获取到锁并开始执行临界区代码。
-
操作系统提供的同步原语:pthread_mutex是基于操作系统提供的同步原语实现的。它依赖于底层的操作系统调用,如futex等。pthread_mutex_lock函数的实现与操作系统提供的同步原语的实现紧密相连,以确保线程在等待锁的时候,CPU不会浪费,同时兼顾等待线程的快速唤醒。
-
优先级反转问题:pthread_mutex锁在实现中存在优先级反转问题。如果锁在高优先级进程中被持有,而低优先级进程需要等待锁,则低优先级进程无法运行,高优先级进程也因为等待其他资源而无法运行。操作系统会根据一些算法判断进程优先级,以确保线程能够快速地获得锁。
总的来说,pthread_mutex是一个由互斥锁和操作系统同步原语构成的基于线程的锁机制,用于保护共享资源的访问。它能够正确地处理优先级反转等问题,确保线程安全地访问共享资源。
pthread_mutex_init
pthread_mutex_init是一个函数,在使用线程时,它是创建线程锁的第一个步骤。线程锁是一种用于多线程编程的同步机制,它是用来保护共享资源,确保在线程要访问共享变量时,只有一个线程进行访问。使用线程锁能够有效地避免竞争条件的发生。
pthread_mutex_init函数主要是用来初始化线程锁,它的函数原型如下:
int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr);
其中,mutex参数是一个指向pthread_mutex_t结构体的指针,用来指定要初始化的线程锁;attr参数是一个可选的指向pthread_mutexattr_t的指针,用来指定线程锁的属性,例如锁的类型、锁的进程共享属性等。
在使用pthread_mutex_init函数时,主要需要注意以下几点:
-
需要在创建线程锁之前调用该函数。
-
如果不需要指定锁的属性,可以将attr参数设置为NULL。
-
函数成功执行后,mutex所指向的pthread_mutex_t结构体将会被初始化为可用的线程锁。
使用pthread_mutex_init函数初始化的线程锁,在使用完毕后一定要记得调用pthread_mutex_destroy函数来销毁线程锁,并且需要确保所有线程都已退出该锁。
综上所述,pthread_mutex_init函数是用来初始化线程锁的重要函数,能够帮助程序员保护共享资源,提高多线程编程的效率和安全性。
pthread_mutex_init函数用于初始化互斥锁,它有两个参数:
-
第一个参数是指向互斥锁变量的指针,用于存储互斥锁的信息。
-
第二个参数是一个指向pthread_mutexattr_t类型的结构体指针,用于设置互斥锁的属性。
在不设置属性时,第二个参数可以设置为NULL。如果设置了属性,则需要使用pthread_mutexattr_init函数对属性进行初始化,并通过pthread_mutexattr_setxxx函数设置属性值,再将属性指针作为参数传递给pthread_mutex_init函数。
- pthread_mutex_init常用的属性包括:
-
PTHREAD_PROCESS_SHARED:互斥锁可以在多个进程间共享。
-
PTHREAD_MUTEX_RECURSIVE:互斥锁是可重入的,同一个线程可以多次获取同一个互斥锁,释放时也要相应多次。
-
PTHREAD_MUTEX_ERRORCHECK:互斥锁是带错误检查的,如果同一个线程多次获取同一个互斥锁,则返回错误码EBUSY。
#include <pthread.h>
pthread_mutex_t mutex;
int main() {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_init(&mutex, &attr);
// 使用互斥锁
...
pthread_mutex_destroy(&mutex);
pthread_mutexattr_destroy(&attr);
return 0;
}
- pthread_mutex_init初始化参数详细介绍:
- PTHREAD_PROCESS_SHARED: 表示创建的互斥锁可以在多个进程间进行共享,即该互斥锁可以被多个进程同时占用和修改。使用该选项需要确保使用的操作系统支持进程间共享,否则将会发生错误。
- PTHREAD_MUTEX_RECURSIVE: 表示创建的互斥锁是可递归的,即同一线程可以多次获取该互斥锁而不会发生死锁。递归锁经常用于模块内部实现,确保模块内部的可重入性。
- PTHREAD_MUTEX_ERRORCHECK: 表示创建的互斥锁是错误检查的,即如果一个线程尝试多次获取同一个互斥锁,则会报错并返回一个错误码。对错误检查不够谨慎的程序可能会出现死锁问题。
- pthread_mutex_init 使用场景如下:
-
PTHREAD_PROCESS_SHARED:多个进程需要协同工作,同时使用共享资源时,需要使用该选项创建可进程共享的互斥锁。
-
PTHREAD_MUTEX_RECURSIVE:在同一个线程内,需要多次获取同一个互斥锁跨内部操作的时候使用。
-
PTHREAD_MUTEX_ERRORCHECK:在多线程应用程序的开发过程中,如果需要确保同一时间只有一个线程可以访问共享资源,但是其它线程尝试获取锁时,出现了错误,则使用该选项可以使应用程序在运行时发现这种错误情况,帮助定位调试问题。
- pthread_mutex_init初始化的锁如果需要在多个进程间共享,应该如何设置
pthread_mutex_init函数创建的锁只能在同一个进程中的线程间共享,如果需要在多个进程间共享锁,需要使用进程间通信(IPC)机制。其中一种方法是使用POSIX命名信号量,可以通过以下步骤实现:
-
使用sem_open函数创建并打开一个命名信号量,指定一个名称和权限(例如O_CREAT | O_EXCL | 0666)。
-
使用pthread_mutexattr_init函数初始化属性。
-
使用pthread_mutexattr_setpshared函数设置属性为PTHREAD_PROCESS_SHARED。
-
使用pthread_mutex_init函数初始化锁,并将属性作为参数传递给函数。
-
在需要使用锁的多个进程中,使用sem_open函数打开同一名称的命名信号量,并将返回的信号量指针作为参数传递给pthread_mutex_init函数。这样在不同进程中的锁就可以使用同一个信号量,从而实现了进程间的共享。
需要注意的是,在使用命名信号量之前应该先清除(unlink)它,以确保该名称尚未被使用。此外,在使用命名信号量时要格外小心,因为信号量是全局共享的。
- pthread_mutex_init初始化的锁为NULL,而不是PTHREAD_PROCESS_SHARED,可以在多个进程间共享吗
不可以。如果使用pthread_mutex_init
初始化的锁为NULL
,则表示创建的是一个进程内的锁(也称为“进程内锁”或“线程锁”),只能在同一个进程内的不同线程之间使用,无法在多个进程之间共享。要在多个进程之间共享锁,需要使用pthread_mutex_init
的PTHREAD_PROCESS_SHARED
参数创建一个“进程间锁”。
pthread_mutex_lock
pthread_mutex_lock函数是POSIX线程库中的一种互斥锁,用于保护共享资源,防止并发访问。这个函数尝试获取互斥锁,如果锁已经被锁住了,则线程会进行阻塞等待锁被释放。
pthread使用示例
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t lock;
void* thread_function(void* arg) {
// 获取互斥锁
pthread_mutex_lock(&lock);
// 现在我们得到了锁,可以安全地访问共享资源
int* p_num = (int*)arg;
printf("Thread %d is accessing shared resource...\n", *p_num);
for(int i = 0; i < 5; i++) {
printf("Thread %d: %d\n", *p_num, i);
sleep(1);
}
printf("Thread %d finished accessing shared resource.\n", *p_num);
// 释放互斥锁
pthread_mutex_unlock(&lock);
return NULL;
}
int main() {
// 初始化互斥锁
pthread_mutex_init(&lock, NULL);
// 创建两个线程
pthread_t thread1, thread2;
int num1 = 1, num2 = 2;
pthread_create(&thread1, NULL, thread_function, &num1);
pthread_create(&thread2, NULL, thread_function, &num2);
// 等待线程结束
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
// 销毁互斥锁
pthread_mutex_destroy(&lock);
return 0;
}
在上面的示例程序中,创建了两个线程,它们都会访问共享资源。在访问共享资源之前,线程会通过pthread_mutex_lock函数获取互斥锁。如果锁已经被另一个线程锁住了,当前线程就会阻塞,直到锁被释放。
当一个线程使用完共享资源之后,会通过pthread_mutex_unlock函数释放互斥锁,以便其他线程可以访问共享资源。在程序结束时,还要通过pthread_mutex_destroy函数销毁互斥锁。
pthread共享锁使用示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#define SHARED_MEM_NAME "/my_shared_mem"
#define SHARED_MEM_SIZE 1024
pthread_mutex_t *mutex;
void *thread_func(void *arg) {
int id = *(int*)arg;
while (1) {
pthread_mutex_lock(mutex);
printf("Thread %d has entered the critical section.\n", id);
sleep(1);
printf("Thread %d is leaving the critical section.\n", id);
pthread_mutex_unlock(mutex);
sleep(2);
}
return NULL;
}
int main() {
int i, fd, *ids;
pthread_t *threads;
struct stat buf;
// 创建或打开共享内存
fd = shm_open(SHARED_MEM_NAME, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("shm_open error");
exit(EXIT_FAILURE);
}
if (ftruncate(fd, SHARED_MEM_SIZE) == -1) {
perror("ftruncate error");
exit(EXIT_FAILURE);
}
if (stat(SHARED_MEM_NAME, &buf) == -1) {
perror("stat error");
exit(EXIT_FAILURE);
}
// 将共享内存映射到进程的地址空间
mutex = mmap(NULL, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mutex == MAP_FAILED) {
perror("mmap error");
exit(EXIT_FAILURE);
}
// 初始化互斥锁
pthread_mutexattr_t mutex_attr;
pthread_mutexattr_init(&mutex_attr);
pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(mutex, &mutex_attr);
// 创建多个线程
threads = malloc(sizeof(pthread_t) * 3);
ids = malloc(sizeof(int) * 3);
for (i = 0; i < 3; i++) {
ids[i] = i + 1;
pthread_create(&threads[i], NULL, thread_func, &ids[i]);
}
// 等待线程完成
for (i = 0; i < 3; i++) {
pthread_join(threads[i], NULL);
}
// 销毁互斥锁
pthread_mutex_destroy(mutex);
pthread_mutexattr_destroy(&mutex_attr);
// 取消映射共享内存
if (munmap(mutex, SHARED_MEM_SIZE) == -1) {
perror("munmap error");
exit(EXIT_FAILURE);
}
// 删除共享内存
if (shm_unlink(SHARED_MEM_NAME) == -1) {
perror("shm_unlink error");
exit(EXIT_FAILURE);
}
return 0;
}
此示例创建三个线程,在其中一个线程运行时,它会获得互斥锁并进入临界区。在两个其他线程的其他并行运行期间,它们无法进入临界区,因为该锁已被占用。当第一个线程完成并释放锁时,其他线程可以获得互斥锁并进入临界区以执行其任务。在这个示例中,所有线程都在打印一些消息后离开临界区。
请注意,示例中使用了pthread_mutexattr_setpshared函数设置了互斥锁属性,以便在多进程环境下使用。此属性允许进程之间共享互斥锁,并且必须在互斥锁初始化之前设置。此外,共享内存的大小预定义为1024字节,可以根据需要进行调整。
pthread+信号量实现共享线程锁
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define NUM_THREADS 5
// 共享变量
int shared_variable = 0;
sem_t shared_variable_lock; // 共享变量的非负整数信号量
void *thread_function(void *thread_id) {
int thread_num = *(int*)thread_id;
int i, tmp;
// 每个线程加锁,修改共享变量,再解锁
for (i = 0; i < 100000; i++) {
sem_wait(&shared_variable_lock);
tmp = shared_variable;
tmp++;
shared_variable = tmp;
sem_post(&shared_variable_lock);
}
printf("Thread %d finished. Shared variable: %d\n", thread_num, shared_variable);
pthread_exit(NULL);
}
int main(int argc, char *argv[]) {
pthread_t threads[NUM_THREADS];
int thread_ids[NUM_THREADS];
int rc, t;
// 初始化非负整数信号量为 1
sem_init(&shared_variable_lock, 0, 1);
// 创建多个线程
for (t = 0; t < NUM_THREADS; t++) {
thread_ids[t] = t;
rc = pthread_create(&threads[t], NULL, thread_function, (void*)&thread_ids[t]);
if (rc) {
printf("Error: return code for pthread_create() is %d\n", rc);
exit(-1);
}
}
// 等待所有线程结束
for (t = 0; t < NUM_THREADS; t++) {
pthread_join(threads[t], NULL);
}
// 销毁信号量并退出
sem_destroy(&shared_variable_lock);
pthread_exit(NULL);
}
该示例创建了 5 个线程,每个线程向共享变量中增加 100000。线程间通过非负整数信号量来同步访问共享变量,保证了线程安全。最终输出共享变量的值。
Semaphore(信号量)实现进程间同步操作
Semaphore(信号量)是一种用于进程间同步和互斥的机制,其主要目的是为了防止多个进程同时访问共享资源而造成竞争,从而导致数据不一致的情况发生。Semaphore可以被用于任何需要同步进程的场景,比如保证文件系统中的文件不被多个进程同时访问等等。
在Linux系统中,Semaphore被定义为一种系统资源,每个进程可以通过semget系统调用来获取一个或多个Semaphore的ID,从而可以使用这些Semaphores在不同的进程之间进行同步。如下所示是使用semget函数来获取Semaphore ID的例子:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc, char *argv[])
{
key_t key;
int semid;
if ((key = ftok(".", 'S')) == -1) {
perror("ftok");
exit(1);
}
if ((semid = semget(key, 1, 0666 | IPC_CREAT)) == -1) {
perror("semget");
exit(1);
}
printf("Semaphore ID : %d\n", semid);
return 0;
}
在上面的例子中,我们首先使用ftok函数创建一个key值,然后使用semget函数来获取一个Semaphore ID。其中key是用来唯一标识Semaphore的,同时也是用来创建Semaphore的。如果Semaphore已经存在,则直接获取Semaphore的ID,如果尚未存在,则会创建一个新的Semaphore。该函数的第一个参数是key值,第二个参数是创建Semaphore的数量,第三个参数是指定Semaphore的权限。
得到Semaphore ID之后,我们可以使用semctl函数来控制Semaphore的行为。 它可以用来设置Semaphore的值,根据需要进行等待/通知,以及删除Semaphore等等。比如下面是一个简单的Semaphore使用的例子,其中使用了semctl函数来实现了两个进程之间的同步:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define SEM_VALUE 1
int main(int argc, char *argv[])
{
key_t key;
int semid, sem_val;
struct sembuf buf;
if ((key = ftok(".", 'S')) == -1) {
perror("ftok");
exit(1);
}
if ((semid = semget(key, 1, 0666 | IPC_CREAT)) == -1) {
perror("semget");
exit(1);
}
if (semctl(semid, 0, SETVAL, SEM_VALUE) == -1) {
perror("semctl");
exit(1);
}
if (fork() == -1) {
perror("fork");
exit(1);
}
buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = SEM_UNDO;
if (semop(semid, &buf, 1) == -1) {
perror("semop");
exit(1);
}
printf("Child process is running now.\n");
buf.sem_op = 1;
if (semop(semid, &buf, 1) == -1) {
perror("semop");
exit(1);
}
printf("Child process completed.\n");
if (wait(NULL) == -1) {
perror("wait");
exit(1);
}
if (semctl(semid, 0, IPC_RMID) == -1) {
perror("semctl");
exit(1);
}
return 0;
}
在上面的例子中,我们首先使用semctl函数来将Semaphore的初始值设置为1。然后使用fork函数创建了一个子进程,并使用Semaphore来同步它们之间的运行。首先子进程执行wait操作并进入阻塞状态,此时父进程恰好通过执行完上面的wait操作。 然后父进程执行一些操作,并调用semop函数增加Semaphore的值,这样就使得子进程不再被阻塞。最后各个进程完成它们的操作后,Semaphore被删除,释放它所使用的系统资源。
以上是一个简单的Semaphore同步操作,使用semget,semctl和semop函数,我们可以利用Semaphore实现进程间同步和互斥操作。
pthread和shmget来实现共享锁
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
pthread_mutex_t *mutex;
void *worker(void *arg) {
pthread_mutex_lock(mutex);
printf("Thread %ld gets the lock!\n", (long) arg);
sleep(2);
printf("Thread %ld releases the lock!\n", (long) arg);
pthread_mutex_unlock(mutex);
return NULL;
}
int main(int argc, char *argv[]) {
int shmid;
key_t key = 1234;
if ((shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666)) < 0) {
perror("shmget");
exit(1);
}
if ((mutex = shmat(shmid, NULL, 0)) == (pthread_mutex_t *) -1) {
perror("shmat");
exit(1);
}
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(mutex, &attr);
pthread_t threads[5];
int i;
for (i = 0; i < 5; i++) {
pthread_create(&threads[i], NULL, worker, (void *) i);
}
for (i = 0; i < 5; i++) {
pthread_join(threads[i], NULL);
}
pthread_mutex_destroy(mutex);
shmdt(mutex);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
在这个示例中,我们使用了pthread和shmget来实现共享锁。主线程首先创建一个共享内存,并将其连接到一个pthread_mutex_t类型的指针变量mutex中。然后,我们使用pthread_mutexattr_t结构来给这个互斥锁设置了PTHREAD_PROCESS_SHARED属性,这样我们就可以使用它了。接下来我们创建了5个线程,并使用pthread_mutex_lock和pthread_mutex_unlock函数来管理这个共享锁。
shmget和shm_open区别
shmget和shm_open都是用来创建共享内存的函数,不过它们有以下几点区别:
- POSIX vs System V
shmget是System V IPC(Inter-Process Communication)机制的一部分,而shm_open是POSIX共享内存机制的一部分。POSIX在开放系统中广泛使用,因此shm_open在现代系统上更常见。
- 名称
shmget使用一个整数键来标识共享内存段,而shm_open使用一个字符串名称来标识共享内存对象。这意味着shm_open更容易使用和识别,因为名称可能会更有描述性,而不仅仅是一个数字。
- 文件句柄
一旦创建了共享内存对象,shm_open将返回一个文件句柄,该文件句柄可以像其他文件一样使用open(), read(), write()等函数。而shmget则只返回一个共享内存ID。
- 访问控制
shm_open提供了更好的权限控制,因为它允许您指定共享内存是否应该对其他用户或组可见,而shmget则没有这种级别的控制。
- 其它
shm_open还允许您将共享内存映射到进程的地址空间中,以便更方便地访问共享内存。此外,shm_open还支持文件锁定和文件映射的内存同步机制。
综上所述,虽然shmget和shm_open都可以用来创建共享内存,但它们有自己的优点和不同的用途。在现代系统中,shm_open更常见,因为它更容易使用和控制,而且还提供了更多的功能。
- 详细介绍
- 用途不同
shmget主要用于创建和获取一个共享内存区域,但是它没有提供文件系统和文件描述符的支持。在使用shmget时,必须使用IPC_KEY作为唯一的标识符来引用共享内存区域。
shm_open主要用于在文件系统中创建或打开一个共享内存对象,它可以使用文件描述符来引用共享内存对象,支持多个进程同时访问。shm_open创建的共享内存对象可以从文件系统中删除,也可以与其他文件描述符共享。
- 调用方式不同
shmget是一个系统调用函数,需要使用IPC_KEY作为参数,返回一个共享内存的标识符。
shm_open是一个库函数,需要指定一个文件名和标志位作为参数,返回一个文件描述符。与shmget不同的是,shm_open的文件名只是文件系统中的一个虚拟的名字,不需要实际的物理文件支持。
- 参数设置不同
在使用shmget时,必须指定共享内存的大小和权限,而在shm_open中,共享内存的大小是通过ftruncate函数设置的。
此外,shm_open还支持一些特殊的标志位,如O_CREAT表示如果该共享内存对象不存在则创建,O_EXCL表示如果共享内存对象已经存在则出错,O_RDWR表示以读写模式打开共享内存对象等。
综合来看,shmget和shm_open都是用于创建共享内存的函数,但是它们在具体用途、调用方式和参数设置方面存在很大的差异。开发者应根据实际需求选择恰当的函数。
thread
pthread是一种POSIX线程库,是一种C语言的多线程编程接口,可以在Linux、Unix、Mac OS等操作系统中使用。而thread是C++11标准中新增加的多线程编程库,是C++语言的多线程编程接口。其区别在于:
-
语言支持:pthread是C语言的线程库,而thread是C++的线程库。
-
功能差异:pthread提供了更底层的线程操作函数,如pthread_create()、pthread_join()等,需要程序员手动管理线程的状态和资源。而thread库提供了更高层次的抽象,如std::thread类,它封装了线程的创建、销毁和等待等操作,使用起来更加方便,避免了手动管理线程状态和资源的繁琐操作。
-
可移植性:pthread是POSIX标准的一部分,可以在各种POSIX兼容的系统上使用。而thread是C++11标准中新增加的特性,需要编译器支持C++11才能使用,在一些老的编译器或操作系统上可能不兼容。
综上所述,pthread更加底层,需要手动管理线程状态和资源,而thread提供了更高层次的抽象,使用更加方便,但需要编译器支持C++11。
- C++11的std::mutex可以使用shm_open打开,从而实现共享锁吗
不可以。C++11的std::mutex是一个内存中的对象,无法通过shm_open打开。shm_open打开的是共享内存区域,它只能用于在进程之间共享内存数据。如果需要在进程之间共享锁,可以使用POSIX的信号量机制。