Linux C++多线程同步的四种方式

Linux C++多线程同步的四种方式,第1张

From : https://blog.csdn.net/qq_39382769/article/details/960753461.同一个线程内部,指令按照先后顺序执行;但不同线程之间的指令很难说清楚是哪一个先执行,在并发情况下,指令执行的先后顺序由内核决定。 如果运行的结果依赖于不同线程执行的先后的话,那么就会形成竞争条件,在这样的情况下,计算的结果很难预知,所以应该尽量避免竞争条件的形成。 2.最常见的解决竞争条件的方法是:将原先分离的两个指令构成一个不可分割的原子 *** 作,而其他任务不能插入到原子 *** 作中! 3.对多线程来说,同步指的是在一定时间内只允许某一个线程访问某个资源,而在此时间内,不允许其他线程访问该资源! 互斥锁 条件变量 读写锁 信号量 一种特殊的全局变量,拥有lock和unlock两种状态。 unlock的互斥锁可以由某个线程获得,一旦获得,这个互斥锁会锁上变成lock状态,此后只有该线程由权力打开该锁,其他线程想要获得互斥锁,必须得到互斥锁再次被打开之后。 1.互斥锁的初始化, 分为静态初始化和动态初始化. 2.互斥锁的相关属性及分类 (1) attr表示互斥锁的属性 (2) pshared表示互斥锁的共享属性,由两种取值: 1)PTHREAD_PROCESS_PRIVATE:锁只能用于一个进程内部的两个线程进行互斥(默认情况) 2)PTHREAD_PROCESS_SHARED:锁可用于两个不同进程中的线程进行互斥,使用时还需要在进程共享内存中分配互斥锁,然后为该互斥锁指定属性就可以了。 互斥锁存在缺点: (1)某个线程正在等待共享数据内某个条件出现。 (2)重复对数据对象加锁和解锁(轮询),但是这样轮询非常耗费时间和资源,而且效率非常低,所以互斥锁不太适合这种情况。 当线程在等待满足某些条件时,使线程进入睡眠状态;一旦条件满足,就换线因等待满足特定条件而睡眠的线程。 程序的效率无疑会大大提高。 1)创建 静态方式:pthread_cond_t cond PTHREAD_COND_INITIALIZER 动态方式:int pthread_cond_init(&cond,NULL) Linux thread 实现的条件变量不支持属性,所以NULL(cond_attr参数) 2)注销 int pthread_cond_destory(&cond) 只有没有线程在该条件变量上,该条件变量才能注销,否则返回EBUSY 因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程!(请参考条件变量的底层实现) 3)等待 条件等待:int pthread_cond_wait(&cond,&mutex) 计时等待:int pthread_cond_timewait(&cond,&mutex,time) 1.其中计时等待如果在给定时刻前条件没有被满足,则返回ETIMEOUT,结束等待 2.无论那种等待方式,都必须有一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait形成竞争条件! 3.在调用pthread_cond_wait前必须由本线程加锁 4)激发 激发一个等待线程:pthread_cond_signal(&cond) 激发所有等待线程:pthread_cond_broadcast(&cond) 重要的是,pthread_cond_signal不会存在惊群效应,也就是是它最多给一个等待线程发信号,不会给所有线程发信号唤醒,然后要求他们自己去争抢资源! pthread_cond_broadcast() 唤醒所有正在pthread_cond_wait()的同一个条件变量的线程。注意:如果等待的多个现场不使用同一个锁,被唤醒的多个线程执行是并发的。pthread_cond_broadcast &pthread_cond_signal1.读写锁比互斥锁更加具有适用性和并行性 2.读写锁最适用于对数据结构的读 *** 作读 *** 作次数多余写 *** 作次数的场合! 3.锁处于读模式时可以线程共享,而锁处于写模式时只能独占,所以读写锁又叫做共享-独占锁。 4.读写锁有两种策略:强读同步和强写同步 强读同步: 总是给读者更高的优先权,只要写者没有进行写 *** 作,读者就可以获得访问权限 强写同步: 总是给写者更高的优先权,读者只能等到所有正在等待或者执行的写者完成后才能进行读 1)初始化的销毁读写锁 静态初始化:pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER 动态初始化:int pthread_rwlock_init(rwlock,NULL),NULL代表读写锁采用默认属性 销毁读写锁:int pthread_rwlock_destory(rwlock) 在释放某个读写锁的资源之前,需要先通过pthread_rwlock_destory函数对读写锁进行清理。释放由pthread_rwlock_init函数分配的资源 如果你想要读写锁使用非默认属性,则attr不能为NULL,得给attr赋值 int pthread_rwlockattr_init(attr),给attr初始化 int pthread_rwlockattr_destory(attr),销毁attr 2)以写的方式获取锁,以读的方式获取锁,释放读写锁 int pthread_rwlock_rdlock(rwlock),以读的方式获取锁 int pthread_rwlock_wrlock(rwlock),以写的方式获取锁 int pthread_rwlock_unlock(rwlock),释放锁 上面两个获取锁的方式都是阻塞的函数,也就是说获取不到锁的话,调用线程不是立即返回,而是阻塞执行,在需要进行写 *** 作的时候,这种阻塞式获取锁的方式是非常不好的,你想一下,我需要进行写 *** 作,不但没有获取到锁,我还一直在这里等待,大大拖累效率 所以我们应该采用非阻塞的方式获取锁: int pthread_rwlock_tryrdlock(rwlock) int pthread_rwlock_trywrlock(rwlock) 互斥锁只允许一个线程进入临界区,而信号量允许多个线程进入临界区。 1)信号量初始化 int sem_init(&sem,pshared, v) pshared为0,表示这个信号量是当前进程的局部信号量。 pshared为1,表示这个信号量可以在多个进程之间共享。 v为信号量的初始值。 返回值: 成功:0,失败:-1 2)信号量值的加减 int sem_wait(&sem):以原子 *** 作的方式将信号量的值减去1 int sem_post(&sem):以原子 *** 作的方式将信号量的值加上1 3)对信号量进行清理 int sem_destory(&sem)

linux多线程

1.线程概述

线程是一个进程内的基本调度单位,也可以称为轻量级进程。线程是在共享内存空间中并发的多道执行路径,它们共享一个进程的资源,如文件描述和信号处理。因此,大大减少了上下文切换的开销。一个进程可以有多个线程,也就

是有多个线程控制表及堆栈寄存器,但却共享一个用户地址空间。

2.线程实现

线程创建pthread_create()

所需头文件#include

<pthread.h>

函数原型int

pthread_create

((pthread_t

*thread,

pthread_attr_t

*attr,

thread:线程标识符

attr:线程属性设置

start_routine:线程函数的起始地址

arg:传递给start_routine的参数

函数返回值

成功:0

出错:-1

线程退出pthread_exit()

所需头文件#include

<pthread.h>

函数原型void

pthread_exit(void

*retval)

函数传入值retval:pthread_exit()调用者线程的返回值,可由其他函数如pthread_join

来检索获取

等待线程退出并释放资源pthread_join()

所需头文件#include

<pthread.h>

函数原型int

pthread_join

((pthread_t

th,

void

**thread_return))

函数传入值

th:等待线程的标识符

thread_return:用户定义的指针,用来存储被等待线程的返回值(不为NULL时)

函数返回值

成功:0

出错:-1

代码举例

1.

#include<pthread.h>

2.

#include<stdio.h>

3.

#include<errno.h>

4.

5.

/*线程1*/

6.

void

thread1()

7.

{

8.

int

i=0

9.

10.

while(1)

11.

{

12.

printf(thread1:%d/n,i)

13.

if(i>3)

14.

pthread_exit(0)

15.

i++

16.

sleep(1)

17.

}

18.

}

19.

20.

/*线程2*/

21.

void

thread2()

22.

{

23.

int

i=0

24.

25.

while(1)

26.

{

27.

printf(thread2:%d/n,i)

28.

if(i>5)

29.

pthread_exit(0)

30.

i++

31.

sleep(1)

32.

}

33.

}

34.

35.

int

main()

36.

{

37.

pthread_t

t1,t2

38.

39.

/*创建线程*/

40.

pthread_create(&t1,NULL,(void

*)thread1,NULL)

41.

pthread_create(&t2,NULL,(void

*)thread2,NULL)

42.

/*等待线程退出*/

43.

pthread_join(t1,NULL)

44.

pthread_join(t2,NULL)

45.

return

0

46.

}

3同步与互斥

<1>互斥锁

互斥锁的 *** 作主要包括以下几个步骤。

互斥锁初始化:pthread_mutex_init

互斥锁上锁:pthread_mutex_lock

互斥锁判断上锁:pthread_mutex_trylock

互斥锁接锁:pthread_mutex_unlock

消除互斥锁:pthread_mutex_destroy

1.

#include<pthread.h>

2.

#include<stdio.h>

3.

#include<errno.h>

4.

5.

int

i=0/*共享变量*/

6.

pthread_mutex_t

mutex=PTHREAD_MUTEX_INITIALIZER/*互斥锁*/

7.

8.

void

thread1()

9.

{

10.

int

ret

11.

while(1)

12.

{

13.

14.

15.

ret=pthread_mutex_trylock(&mutex)/*判断上锁*/

16.

17.

if(ret!=EBUSY)

18.

{

19.

pthread_mutex_lock(&mutex)/*上锁*/

20.

printf(This

is

thread1:%d/n,i)

21.

i++

22.

pthread_mutex_unlock(&mutex)/*解锁*/

23.

}

24.

sleep(1)

25.

}

26.

}

27.

28.

void

thread2()

29.

{int

ret

30.

while(1)

31.

{

32.

33.

ret=pthread_mutex_trylock(&mutex)

34.

if(ret!=EBUSY)

35.

{

36.

pthread_mutex_lock(&mutex)

37.

printf(This

is

thread2:%d/n,i)

38.

i++

39.

pthread_mutex_unlock(&mutex)

40.

}

41.

sleep(1)

42.

}

43.

}

44.

int

main()

45.

{

46.

pthread_t

t1,t2

47.

pthread_mutex_init(&mutex,NULL)

48.

pthread_create(&t1,NULL,(void

*)thread1,NULL)

49.

pthread_create(&t2,NULL,(void

*)thread2,NULL)

50.

51.

pthread_join(t1,NULL)

52.

pthread_join(t2,NULL)

53.

54.

pthread_mutex_destroy(&mutex)

55.

return

0

56.

}

<2>信号量

未进行同步处理的两个线程

1.

#include<pthread.h>

2.

#include<stdio.h>

3.

#include<errno.h>

4.

5.

int

i=0

6.

void

thread1()

7.

{

8.

9.

while(1)

10.

{

11.

printf(This

is

thread1:%d/n,i)

12.

i++

13.

sleep(1)

14.

}

15.

}

16.

17.

18.

void

thread2()

19.

{

20.

21.

while(1)

22.

{

23.

printf(This

is

thread2:%d/n,i)

24.

i++

25.

sleep(1)

26.

}

27.

}

28.

29.

int

main()

30.

{

31.

pthread_t

t1,t2

32.

33.

pthread_create(&t1,NULL,(void

*)thread1,NULL)

34.

pthread_create(&t2,NULL,(void

*)thread2,NULL)

线程同步有几种实现方法,都是什么?

答:多线程有两种实现方法,分别是继承Thread类与实现Runnable接口

同步的实现方面有两种,分别是synchronized,wait与notify

线程同步的几种方法的总结

线程同步的方式包括:互斥锁、读写锁、条件变量、信号量和令牌。互斥锁和读写锁:提供对临界资源的保护,当多线程试图访问临界资源时,都必须通过获取锁的方式来访问临界资源。(临界资源:是被多线程共享的资源)当读写线程获取锁的频率差别不大时,一般采用互斥锁,如果读线程访问临界资源的频率大于写线程,这个时候采用读写锁较为合适,读写锁允许多个读线程同时访问临界资源,读写线程必须互斥访问临界资源。读写锁的实现采用了互斥锁,所以在读写次数差不多的情况下采用读写锁性能没有直接采用互斥锁来的高。条件变量:提供线程之间的一种通知机制,当某一条件满足时,线程A可以通知阻塞在条件变量上的线程B,B所期望的条件已经满足,可以解除在条件变量上的阻塞 *** 作,继续做其他事情。信号量:提供对临界资源的安全分配。如果存在多份临界资源,在多个线程争抢临界资源的情况下,向线程提供安全分配临界资源的方法。如果临界资源的数量为1,将退化为锁。令牌:一种高级的线程同步的方法。它既提供锁的安全访问临界资源的功能,又利用了条件变量使得线程争夺临界资源时是有序的。下面提供Token在ACE中的一种实现方法。可以从下面的类图,可以看到ACE是如何设计Token的。 ACE_Token:这个类是Token类,提供了获取和释放Token的方法。对于Token的获取策略,ACE提供两种实现:FIFO和LIFO。Token中对应着两个队列,一个是获取Token是为了写的队列,另外一个是获取Token是为了读的队列。ACE_Token_Queue:队列是一个链表,该类提供对链表的管理 *** 作。ACE_Token_Entry:是队列中存放的元素,提供了对条件变量的封装,一个元素代表一个线程试图获取Token。如果Token已经被获取,线程需要阻塞在自己的Token上(队列Entry中的条件变量上)。等待Token持有者释放该Token,并通知阻塞的线程。 下面对ACE_Token中的组要方法提供分析。 获取判断是否有线程已经持有互斥锁了,如果没人持有,表示临界资源是可用的,那么可以立即返回成功。检查是不是线程尝试递归获取互斥锁。因为Token支持这种情况,所以也可立即返回成功。同时将nesting计数器自增。创建一个Token Entry,并将其排入队列。调用用户自定义的Hook方法,在线程进行sleep之前,用户可以调用自定义的Hook方法。线程睡眠,如果线程唤醒后,发现当前线程不是Token的拥有者将继续睡眠。线程被唤醒后,将Token entry从队列中删除。释放如果发现嵌套层数大于0,需要将嵌套层数的计数器减一,然后让该线程继续持有Token。否则,该线程负责从队列中按照一定的策略,取出Token entry,并通过该Entry通知阻塞在Entry上的线程,资源被释放,你可以使用资源了。

请用技术语言介绍下线程同步,并发 *** 作怎么控制

现在流行的进程线程同步互斥的控制机制,其实是由最原始最基本的4种方法实现的。由这4种方法组合优化就有了.Net和Java下灵活多变的,编程简便的线程进程控制手段。

这4种方法具体定义如下 在《 *** 作系统教程》ISBN 7-5053-6193-7 一书中可以找到更加详细的解释

1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。

2、互斥量:为协调共同对一个共享资源的单独访问而设计的。

3、信号量:为控制一个具有有限数量用户资源而设计。

4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。

临界区(Critical Section)

保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式 *** 作共享资源的目的。

临界区包含两个 *** 作原语:

EnterCriticalSection() 进入临界区

LeaveCriticalSection() 离开临界区

EnterCriticalSection()语句执行后代码将进入临界区以后无论发生什么,必须确保与之匹配的LeaveCriticalSection()都能够被执行到。否则临界区保护的共享资源将永远不会被释放。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。

MFC提供了很多功能完备的类,我用MFC实现了临界区。MFC为临界区提供有一个CCriticalSection类,使用该类进行线程同步处理是非常简单的。只需在线程函数中用CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片段即可。Lock()后代码用到的资源自动被视为临界区内的资源被保护。UnLock后别的线程才能访问这些资源。

CriticalSection

CCriticalSection global_CriticalSection

共享资源

char global_Array[256]

初始化共享资源

void InitializeArray()

{

for(int i = 0i<256i++)

{

global_Array[i]=I

}

}

写线程

UINT Global_ThreadWrite(LPVOID pParam)

{

CEdit *ptr=(CEdit *)pParam

ptr->SetWindowText("")

进入临界区

global_CriticalSection.Lock()

for(int i = 0i<256i++)

{

global_Array[i]=W

ptr->SetWindowText(global_Array)

Sleep(10)

}

离开临界区

global_CriticalSection.Unlock()

return 0

}

删除线程

UINT Global_ThreadDelete(LPVOID pParam)

{

CEdit *ptr=(CE......>>

C语言怎么实现多线程同步

使用pthread库执行多线程,这个是Linux下的线程库 Windows下应该有自己的API,不过这种东西一般还是以Linux为标准。pthread_create()创建一个线程,传入fun()的函数指针就行了。

然后这个Beep()的需求要进行线程间通信,可以用共享内存的方法,设一个bool变量flag共享,然后beep的时候设为false,beep完设成true。fun()里面每次看一下这个flag,是false的话就不做动作等下一秒,基本可以满足需求。

这样做的好处是实现简单,但时间是以1s为单位的。如果要8秒结束立刻执行,需要用条件变量的方法来控制,比较复杂,这样的实现方式一个可以满足需求了。

Java线程同步的方法

等待唤醒机制

wait():让线程等待。将线程存储到一个线程池中。

notify():唤醒被等待的线程。通常都唤醒线程池中的第一个。让被唤醒的线程处于临时阻塞状态。

notifyAll(): 唤醒所有的等待线程。将线程池中的所有线程都唤醒,让它们从冻结状体转到临时阻塞状态.

这三个方法用于 *** 作线程,可是定义在了Object类中,为什么呢?

因为,这三个方法在使用时,都需要定义在同步中,要明确这些方法所 *** 作的线程所属于锁。

简单说。在A锁被wait的线程,只能被A锁的notify方法唤醒。

所以必须要表示wait notify方法所属的锁对象,而锁对象可以是任意的对象。

可以被任意的对象调用的方法肯定弧义在Object类中。

注意:等待唤醒机制,通常都用在同步中,因为需要锁的支持。

而且必须要明确wait notify 所作用的锁对象。

JDK1.5后的锁

在jdk1.5版本之后,

出现了一些新的特性,将原理的线程进行了改良。

在java.util.concurrent.locks包中提供了一个接口Lock。替代了synchronized。

synchronized。使用的是锁 *** 作是隐式的。

Lock接口,使用的锁 *** 作是显示的。

由两个方法来完成:

lock():获取锁。

unlock():释放锁。

还有一个对象,Condition.

该对象的出现替代了Object中的wait notify notifyAll这些 *** 作监视器的方法。

替代后的方式:await signal signalAll.

线程同步互斥的4种方式

临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、事件(Event)的区别

1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。

2、互斥量:采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享

3、信号量:它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目

4、事 件: 通过通知 *** 作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的 *** 作

如何实现序列化?如何实现线程的同步

在java5以前实现多线程有两种方法(继承Thread类和实现Runnable接口)

它们分别为:

使用new Thread()和new Thread(Runnable)形式

第一种直接调用thread的run方法,所以,往往使用Thread子类,即new SubThread()。

第二种调用

Runnable的run方法。

第一种:

new Thread(){}.start()这表示调用Thread子类对象的run方法,new Thread(){}表示一个Thread的匿名子类的实例对象,子类加上run方法后的代码如下:

new Thread(){

public void run(){

}

}.start()

第二种:

new Thread(

new Runnable(){}

).start()

这表示调用Thread对象接受的Runnable对象的run方法,new Runnable(){}表示一个Runnable的匿名子类的实例对象,

runnable的子类加上run方法后的代码如下:

new Thread(new Runnable(){

public void run(){

}

}

).start()

线程同步有几种实现方法,都是什么

继承Thread 类与实现Runnable 接口都能实现多线程

synchronized,wait 与notify 可以实现同步

实现线程同步的话在java中可以怎么实现

用synchronize关键字,可以有代码段加锁,对象加锁,方法加锁等方法实现线程同步


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8981594.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-23
下一篇 2023-04-23

发表评论

登录后才能评论

评论列表(0条)

保存