深入解析Linux C多线程同步技术 (linux c多线程同步)

随着计算机系统的不断提升,多核CPU逐渐成为高性能计算机的核心部分,多线程编程也逐渐成为软件开发领域的热门技术之一。在多线程编程中,线程同步是一个必不可少的操作,而Linux C多线程同步技术是实现线程同步的关键。

本文将围绕Linux C多线程同步技术展开深入解析,主要包括以下几个方面:多进程同步机制、多线程同步机制、pthread库的使用、互斥锁、条件变量、信号量等内容。

一、多进程同步机制

在Linux操作系统中,多进程之间的通信和同步涉及到诸多机制,例如管道、socket、消息队列、共享内存等。其中,共享内存是一种特殊的内存区域,可以被多个进程进行访问,从而实现进程之间的通信和同步。

共享内存是一种直接通信机制,因为进程可以直接访问共享内存,而不需要进行系统调用。同时,共享内存机制提供了一些同步机制,例如信号量、互斥锁等,可用于控制进程之间对共享内存数据的访问顺序。

二、多线程同步机制

Linux操作系统中,线程同步也涉及到各种机制,例如进程间通信机制、互斥锁、条件变量、信号量等。

在多线程编程中,互斥锁是最常用的同步机制之一,用于控制对共享资源的访问。具体来说,互斥锁在对某一共享资源进行操作时,先加锁,操作完成后再释放锁,从而保证同一时刻只有一个线程对共享资源进行访问,避免了多个线程同时修改共享资源,从而导致数据不一致的问题。

此外,条件变量用于线程间的等待和通知。一般情况下,条件变量和互斥锁一起使用,因为锁能够保证线程间的互斥操作,而条件变量则提供了一种线程间的等待和通知机制。

信号量是一种机制,用于控制对一组共享资源的访问。当一个线程需要进行对共享资源的访问时,它请求信号量,如果信号量的计数器大于0,那么线程就可以访问共享资源,同时信号量的计数器将减一。如果信号量计数器为0,那么线程将处于等待状态,直到其他线程释放了信号量,计数器才会增加,使得线程可以访问到共享资源。

三、pthread库的使用

Linux C多线程编程中,pthread库是最基础的库之一,可以实现各种线程操作,例如创建线程、等待线程、终止线程等。常用的线程函数包括:pthread_create、pthread_join、pthread_attr_init、pthread_attr_destroy等。

pthread_create函数可用于创建线程,它的原型如下:

“`c

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg)

“`

其中,thread参数是创建线程的ID,attr参数代表线程的属性,start_routine函数是线程的入口函数,arg参数是传递给线程入口函数的参数。

pthread_join函数是用于等待线程结束的函数,其原型如下:

“`c

int pthread_join(pthread_t thread, void **retval)

“`

其中,thread参数代表需要等待的线程ID,retval参数为指向线程的退出状态的指针。如果线程已经结束,则pthread_join函数立即返回。

pthread_attr_init函数用于初始化线程属性,pthread_attr_destroy函数用于释放线程属性所占用的资源。

四、互斥锁

Linux C多线程编程中,互斥锁是常用的同步机制之一。Linux提供了两种类型的互斥锁:PTHREAD_MUTEX_NORMAL(普通互斥锁)和PTHREAD_MUTEX_RECURSIVE(递归互斥锁)。

普通互斥锁只能被解锁一次,如果一个线程将普通互斥锁解锁多次,将导致未定义的行为。

递归互斥锁可以被同一线程多次加锁,每次加锁时计数器加一,解锁时计数器减一,只有计数器为0时,才能被其他线程加锁。

互斥锁使用时,首先需要初始化互斥锁,然后使用pthread_mutex_lock函数加锁,再使用pthread_mutex_unlock函数释放锁,如下所示:

“`c

pthread_mutex_t mutex;

void func() {

pthread_mutex_init(&mutex, NULL);

pthread_mutex_lock(&mutex);

// 此处进行临界区操作

pthread_mutex_unlock(&mutex);

pthread_mutex_destroy(&mutex);

}

“`

五、条件变量

Linux C多线程编程中,条件变量是线程间等待和通知的一种机制。条件变量的类型为pthread_cond_t,可以通过pthread_cond_init函数来初始化一个条件变量。

在使用条件变量时,通常需要与互斥锁一起使用。因为条件变量本身并不提供锁机制,可能会出现多个线程同时访问的问题。当一个线程需要等待条件变量时,应该先加锁,然后再等待条件变量的信号通知,如下所示:

“`c

pthread_mutex_t mutex;

pthread_cond_t cond;

void *func(void *arg) {

pthread_mutex_lock(&mutex);

while (condition) {

pthread_cond_wt(&cond, &mutex);

}

pthread_mutex_unlock(&mutex);

}

“`

在其它线程中,可以通过pthread_cond_signal或pthread_cond_broadcast函数来发送信号通知,如下所示:

“`c

pthread_mutex_t mutex;

pthread_cond_t cond;

int condition = 0;

void *func(void *arg) {

pthread_mutex_lock(&mutex);

condition = 1;

pthread_cond_signal(&cond); // 发送信号通知

pthread_mutex_unlock(&mutex);

}

“`

六、信号量

Linux C多线程编程中,信号量是一个最基本的同步机制,也是多进程编程中最常用的同步手段。信号量是一种计数器,用于控制对一组共享资源的访问。具体来说,当一个线程需要访问共享资源时,它向信号量请求一个锁,如果计数器大于0,那么线程就可以访问共享资源,同时计数器减一,如果计数器为0,那么线程将等待信号量的值发生变化。

Linux中提供了两种类型的信号量:二进制信号量和计数信号量。二进制信号量只有两种状态,一种是0,表示资源不可用,另一种是1,表示资源可用。计数信号量则是根据计数器的值来控制对共享资源的访问,该值通常初始化为n,表示共享资源有n个。

在使用信号量时,首先需要创建信号量,然后使用sem_wt函数等待信号量,使用sem_post函数释放信号量。

七、

本文主要围绕Linux C多线程同步技术展开深入解析,分别从多进程同步机制、多线程同步机制、pthread库的使用、互斥锁、条件变量、信号量等方面进行介绍。同步机制是多线程编程中必不可少的部分,合理地使用同步机制可以有效避免线程之间的竞争和冲突,从而保证程序的正确性和稳定性。

相关问题拓展阅读:

linux 多进程信号同步问题

朋友你好:希望能帮到你。互相学习。

线程的更大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。清敏linux下提供了多种方式来处理线程裂做同步,最常用的是互斥锁、条件变量和信号量。

1)互斥锁(mutex)

通过锁机制实现线程间的同步。同一时刻只允许一个肆正衡线程执行一个关键部分的代码。

int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);

int pthread_mutex_lock(pthread_mutex *mutex);

int pthread_mutex_destroy(pthread_mutex *mutex);

int pthread_mutex_unlock(pthread_mutex *

(1)先初始化锁init()或静态赋值pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIER

attr_t有:

PTHREAD_MUTEX_TIMED_NP:其余线程等待队列

PTHREAD_MUTEX_RECURSIVE_NP:嵌套锁,允许线程多次加锁,不同线程,解锁后重新竞争

PTHREAD_MUTEX_ERRORCHECK_NP:检错,与一同,线程请求已用锁,返回EDEADLK;

PTHREAD_MUTEX_ADAPTIVE_NP:适应锁,解锁后重新竞争

(2)加锁,lock,trylock,lock阻塞等待锁,trylock立即返回EBUSY

(3)解锁,unlock需满足是加锁状态,且由加锁线程解锁

(4)清除锁,destroy(此时锁必需unlock,否则返回EBUSY,//Linux下互斥锁不占用内存资源

示例代码

#include

#include

#include

#include

#include “iostream”

using namespace std;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int tmp;

void* thread(void *arg)

{

cout

#include

#include “stdlib.h”

#include “unistd.h”

pthread_mutex_t mutex;

pthread_cond_t cond;

void hander(void *arg)

{

free(arg);

(void)pthread_mutex_unlock(&mutex);

}

void *thread1(void *arg)

{

pthread_cleanup_push(hander, &mutex);

while(1)

{

printf(“thread1 is running\n”);

pthread_mutex_lock(&mutex);

pthread_cond_wait(&cond,&mutex);

printf(“thread1 applied the condition\n”);

pthread_mutex_unlock(&mutex);

sleep(4);

}

pthread_cleanup_pop(0);

}

void *thread2(void *arg)

{

while(1)

{

printf(“thread2 is running\n”);

pthread_mutex_lock(&mutex);

pthread_cond_wait(&cond,&mutex);

printf(“thread2 applied the condition\n”);

pthread_mutex_unlock(&mutex);

sleep(1);

}

}

int main()

{

pthread_t thid1,thid2;

printf(“condition variable study!\n”);

pthread_mutex_init(&mutex,NULL);

pthread_cond_init(&cond,NULL);

pthread_create(&thid1,NULL,thread1,NULL);

pthread_create(&thid2,NULL,thread2,NULL);

sleep(1);

do

{

pthread_cond_signal(&cond);

}while(1);

sleep(20);

pthread_exit(0);

return 0;

}

示例程序2:

#include

#include

#include “stdio.h”

#include “stdlib.h”

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

struct node

{

int n_number;

struct node *n_next;

} *head = NULL;

/**/

static void cleanup_handler(void *arg)

{

printf(“Cleanup handler of second thread./n”);

free(arg);

(void)pthread_mutex_unlock(&mtx);

}

static void *thread_func(void *arg)

{

struct node *p = NULL;

pthread_cleanup_push(cleanup_handler, p);

while (1)

{

//这个mutex主要是用来保证pthread_cond_wait的并发性

pthread_mutex_lock(&mtx);

while (head == NULL)

{

//这个while要特别说明一下,单个pthread_cond_wait功能很完善,为何

//这里要有一个while (head == NULL)呢?因为pthread_cond_wait里的线

//程可能会被意外唤醒,如果这个时候head != NULL,则不是我们想要的情况。

//这个时候,应该让线程继续进入pthread_cond_wait

// pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,

//然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立

//而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);,再读取资源

//用这个流程是比较清楚的/*block–>unlock–>wait() return–>lock*/

pthread_cond_wait(&cond, &mtx);

p = head;

head = head->n_next;

printf(“Got %d from front of queue/n”, p->n_number);

free(p);

}

pthread_mutex_unlock(&mtx); //临界区数据操作完毕,释放互斥锁

}

pthread_cleanup_pop(0);

return 0;

}

int main(void)

{

pthread_t tid;

int i;

struct node *p;

//子线程会一直等待资源,类似生产者和消费者,但是这里的消费者可以是多个消费者,而

//不仅仅支持普通的单个消费者,这个模型虽然简单,但是很强大

pthread_create(&tid, NULL, thread_func, NULL);

sleep(1);

for (i = 0; i n_number = i;

pthread_mutex_lock(&mtx); //需要操作head这个临界资源,先加锁,

p->n_next = head;

head = p;

pthread_cond_signal(&cond);

pthread_mutex_unlock(&mtx); //解锁

sleep(1);

}

printf(“thread 1 wanna end the line.So cancel thread 2./n”);

//关于pthread_cancel,有一点额外的说明,它是从外部终止子线程,子线程会在最近的取消点,退出

//线程,而在我们的代码里,最近的取消点肯定就是pthread_cond_wait()了。

pthread_cancel(tid);

pthread_join(tid, NULL);

printf(“All done — exiting/n”);

return 0;

}

3)信号量

如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。

信号量函数的名字都以”sem_”打头。线程使用的基本信号量函数有四个。

#include

int sem_init (sem_t *sem , int pshared, unsigned int value);

这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。

两个原子操作函数:

int sem_wait(sem_t *sem);

int sem_post(sem_t *sem);

这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。

sem_post:给信号量的值加1;

sem_wait:给信号量减1;对一个值为0的信号量调用sem_wait,这个函数将会等待直到有其它线程使它不再是0为止。

int sem_destroy(sem_t *sem);

这个函数的作用是再我们用完信号量后都它进行清理。归还自己占有的一切资源。

示例代码:

#include

#include

#include

#include

#include

#include

#define return_if_fail(p) if((p) == 0){printf (“:func error!/n”, __func__);return;}

typedef struct _PrivInfo

{

sem_t s1;

sem_t s2;

time_t end_time;

}PrivInfo;

static void info_init (PrivInfo* thiz);

static void info_destroy (PrivInfo* thiz);

static void* pthread_func_1 (PrivInfo* thiz);

static void* pthread_func_2 (PrivInfo* thiz);

int main (int argc, char** argv)

{

pthread_t pt_1 = 0;

pthread_t pt_2 = 0;

int ret = 0;

PrivInfo* thiz = NULL;

thiz = (PrivInfo* )malloc (sizeof (PrivInfo));

if (thiz == NULL)

{

printf (“: Failed to malloc priv./n”);

return -1;

}

info_init (thiz);

ret = pthread_create (&pt_1, NULL, (void*)pthread_func_1, thiz);

if (ret != 0)

{

perror (“pthread_1_create:”);

}

ret = pthread_create (&pt_2, NULL, (void*)pthread_func_2, thiz);

if (ret != 0)

{

perror (“pthread_2_create:”);

}

pthread_join (pt_1, NULL);

pthread_join (pt_2, NULL);

info_destroy (thiz);

return 0;

}

static void info_init (PrivInfo* thiz)

{

return_if_fail (thiz != NULL);

thiz->end_time = time(NULL) + 10;

sem_init (&thiz->s1, 0, 1);

sem_init (&thiz->s2, 0, 0);

return;

}

static void info_destroy (PrivInfo* thiz)

{

return_if_fail (thiz != NULL);

sem_destroy (&thiz->s1);

sem_destroy (&thiz->s2);

free (thiz);

thiz = NULL;

return;

}

static void* pthread_func_1 (PrivInfo* thiz)

{

return_if_fail (thiz != NULL);

while (time(NULL) end_time)

{

sem_wait (&thiz->s2);

printf (“pthread1: pthread1 get the lock./n”);

sem_post (&thiz->s1);

printf (“pthread1: pthread1 unlock/n”);

sleep (1);

}

return;

}

static void* pthread_func_2 (PrivInfo* thiz)

{

return_if_fail (thiz != NULL);

while (time (NULL) end_time)

{

sem_wait (&thiz->s1);

printf (“pthread2: pthread2 get the unlock./n”);

sem_post (&thiz->s2);

printf (“pthread2: pthread2 unlock./n”);

sleep (1);

}

return;

}

通 过执行结果后,可以看出,会先执行线程二的函数,然后再执行线程一的函数。它们两就实现了同步

返回列表

上一篇:高中心理素质情况描述300

关于linux c多线程同步的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » 深入解析Linux C多线程同步技术 (linux c多线程同步)