随着计算机系统的不断提升,多核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库的使用、互斥锁、条件变量、信号量等方面进行介绍。同步机制是多线程编程中必不可少的部分,合理地使用同步机制可以有效避免线程之间的竞争和冲突,从而保证程序的正确性和稳定性。
成都网站建设公司-创新互联,建站经验丰富以策略为先导10多年以来专注数字化网站建设,提供企业网站建设,高端网站设计,响应式网站制作,设计师量身打造品牌风格,热线:028-86922220朋友你好:希望能帮到你。互相学习。
线程的更大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。清敏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;
}
通 过执行结果后,可以看出,会先执行线程二的函数,然后再执行线程一的函数。它们两就实现了同步
创新互联【028-86922220】值得信赖的成都网站建设公司。多年持续为众多企业提供成都网站建设,成都品牌建站设计,成都高端网站制作开发,SEO优化排名推广服务,全网营销让企业网站产生价值。
分享文章:深入解析LinuxC多线程同步技术(linuxc多线程同步)
分享地址:http://www.csdahua.cn/qtweb/news47/426997.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网