-
2018-08-08 10:11:19
进程中线程同步的四种常用方式:
一、 临界区(CCriticalSection)
当多个线程访问一个独占性共享资源时,可以使用临界区对象。拥有临界区的线程可以访问被保护起来的资源或代码段,其他线程若想访问,则被挂起,直到拥有临界区的线程放弃临界区为止。具体应用方式:
1、 定义临界区对象CcriticalSection g_CriticalSection;
2、 在访问共享资源(代码或变量)之前,先获得临界区对象,g_CriticalSection.Lock();
3、 访问共享资源后,则放弃临界区对象,g_CriticalSection.Unlock();
二、 事件(CEvent)
事件机制,则允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。比如在某些网络应用程序中,一个线程如A负责侦听通信端口,另外一个线程B负责更新用户数据,利用事件机制,则线程A可以通知线程B何时更新用户数据。每个Cevent对象可以有两种状态:有信号状态和无信号状态。Cevent类对象有两种类型:人工事件和自动事件。
自动事件对象,在被至少一个线程释放后自动返回到无信号状态;
人工事件对象,获得信号后,释放可利用线程,但直到调用成员函数ReSet()才将其设置为无信号状态。在创建Cevent对象时,默认创建的是自动事件。
1、
1
2
3
4
CEvent(
BOOL
bInitiallyOwn=FALSE,
BOOL
bManualReset=FALSE,
LPCTSTR
lpszName=NULL,
LPSECURITY_ATTRIBUTES lpsaAttribute=NULL);
- bInitiallyOwn:指定事件对象初始化状态,TRUE为有信号,FALSE为无信号;
- bManualReset:指定要创建的事件是属于人工事件还是自动事件。TRUE为人工事件,FALSE为自动事件;
- 后两个参数一般设为NULL,在此不作过多说明。
2、BOOL CEvent::SetEvent();
将Cevent类对象的状态设置为有信号状态。如果事件是人工事件,则Cevent类对象保持为有信号状态,直到调用成员函数ResetEvent()将其重新设为无信号状态时为止。如果为自动事件,则在SetEvent()后将事件设置为有信号状态,由系统自动重置为无信号状态。
3、BOOL CEvent::ResetEvent();
将事件的状态设置为无信号状态,并保持该状态直至SetEvent()被调用为止。由于自动事件是由系统自动重置,故自动事件不需要调用该函数。
一般通过调用WaitForSingleObject()函数来监视事件状态。
三、 互斥量(CMutex)
互斥对象和临界区对象非常相似,只是其允许在进程间使用,而临界区只限制与同一进程的各个线程之间使用,
但是更节省资源,更有效率。
四、 信号量(CSemphore)
当需要一个计数器来限制可以使用某共享资源的线程数目时,可以使用“信号量”对象。CSemaphore类对象保存了对当前访问某一个指定资源的线程的计数值,该计数值是当前还可以使用该资源的线程数目。如果这个计数达到了零,则所有对这个CSemaphore类对象所控制的资源的访问尝试都被放入到一个队列中等待,直到超时或计数值不为零为止。
CSemaphore 类的构造函数原型及参数说明如下:
1
2
3
4
5
6
CSemaphore(
LONG
lInitialCount = 1,
LONG
lMaxCount = 1,
LPCTSTR
pstrName = NULL,
LPSECURITY_ATTRIBUTES lpsaAttributes = NULL
);
- lInitialCount:信号量对象的初始计数值,即可访问线程数目的初始值;
- lMaxCount:信号量对象计数值的最大值,该参数决定了同一时刻可访问由信号量保护的资源的线程最大数目;
- 后两个参数在同一进程中使用一般为NULL,不作过多讨论;
一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就减1,只要当前可用资源计数大于0,就可以发出信号量信号。如果为0,则放入一个队列中等待。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源数加1。
1
2
3
BOOL
ReleaseSemaphore(
HANDLE
hSemaphore,
// hSemaphore:信号量句柄
LONG
lReleaseCount,
// lReleaseCount:信号量计数值
LPLONG
lpPreviousCount
// 参数一般为NULL);
更多相关内容 -
python的多线程及线程同步方式
2019-07-17 09:37:231.线程执行 join与setDaemon 1.子线程在主线程运行结束后,会继续执行完,如果给子线程设置为守护线程(setDaemon=True),主线程运行结束子线程即结束; 2 .如果join()线程,那么主线程会等待子线程执行完再执行...1.线程执行
join与setDaemon
-
1.子线程在主线程运行结束后,会继续执行完,如果给子线程设置为守护线程(setDaemon=True),主线程运行结束子线程即结束;
-
2 .如果join()线程,那么主线程会等待子线程执行完再执行。
import threading import time def get_thread_a(): print("get thread A started") time.sleep(3) print("get thread A end") def get_thread_b(): print("get thread B started") time.sleep(5) print("get thread B end") if __name__ == "__main__": thread_a = threading.Thread(target=get_thread_a) thread_b = threading.Thread(target=get_thread_b) start_time = time.time() thread_b.setDaemon(True) thread_a.start() thread_b.start() thread_a.join() end_time = time.time() print("execution time: {}".format(end_time - start_time))
thread_a是
join
,首先子线程thread_a执行,thread_b是守护线程,当主线程执行完后,thread_b不会再执行执行结果如下:
get thread A started get thread B started get thread A end execution time: 3.003199815750122
2.线程同步
多线程间共享全局变量,多个线程对该变量执行不同的操作时,该变量最终的结果可能是不确定的(每次线程执行后的结果不同),如:对count变量执行加减操作 ,count的值是不确定的,要想count的值是一个确定的需对线程执行的代码段加锁。
3.线程同步的方式
3.1 锁机制
python对线程加锁主要有Lock和Rlock模块Lock:
from threading import Lock lock = Lock() lock.acquire() lock.release()
Lock有acquire()和release()方法,这两个方法必须是成对出现的,acquire()后面必须release()后才能再acquire(),否则会造成死锁
Rlock:
鉴于Lock可能会造成死锁的情况,RLock(可重入锁)对Lock进行了改进,RLock可以在同一个线程里面连续调用多次acquire(),但必须再执行相同次数的release()
from threading import RLock lock = RLock() lock.acquire() lock.acquire() lock.release() lock.release()
当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“同步阻塞”(参见多线程的基本概念)。
直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。
3.2 Semaphore(信号量)
信号量也提供acquire方法和release方法,每当调用acquire方法的时候,如果内部计数器大于0,则将其减1,如果内部计数器等于0,则会阻塞该线程,直到有线程调用了release方法将内部计数器更新到大于1位置。
Semaphore(信号量)是计算机科学史上最古老的同步指令之一。Semaphore管理一个内置的计数器,每当调用acquire()时-1,调用release() 时+1。计数器不能小于0;当计数器为0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。
基于这个特点,Semaphore经常用来同步一些有“访客上限”的对象,比如连接池。
BoundedSemaphore 与Semaphore的唯一区别在于前者将在调用release()时检查计数器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。
构造方法:
Semaphore(value=1): value是计数器的初始值。import time import threading def get_thread_a(semaphore,i): time.sleep(1) print("get thread : {}".format(i)) semaphore.release() def get_thread_b(semaphore): for i in range(10): semaphore.acquire() thread_a = threading.Thread(target=get_thread_a, args=(semaphore,i)) thread_a.start() if __name__ == "__main__": semaphore = threading.Semaphore(2) thread_b = threading.Thread(target=get_thread_b, args=(semaphore,)) thread_b.start()
3.3 条件判断
所谓条件变量,即这种机制是在满足了特定的条件后,线程才可以访问相关的数据。
它使用Condition类来完成,由于它也可以像锁机制那样用,所以它也有acquire方法和release方法,而且它还有wait,notify,notifyAll方法。
""" 一个简单的生产消费者模型,通过条件变量的控制产品数量的增减,调用一次生产者产品就是+1,调用一次消费者产品就会-1. """ """ 使用 Condition 类来完成,由于它也可以像锁机制那样用,所以它也有 acquire 方法和 release 方法,而且它还有 wait, notify, notifyAll 方法。 """ import threading import queue,time,random class Goods:#产品类 def __init__(self): self.count = 0 def add(self,num = 1): self.count += num def sub(self): if self.count>=0: self.count -= 1 def empty(self): return self.count <= 0 class Producer(threading.Thread):#生产者类 def __init__(self,condition,goods,sleeptime = 1):#sleeptime=1 threading.Thread.__init__(self) self.cond = condition self.goods = goods self.sleeptime = sleeptime def run(self): cond = self.cond goods = self.goods while True: cond.acquire()#锁住资源 goods.add() print("产品数量:",goods.count,"生产者线程") cond.notifyAll()#唤醒所有等待的线程--》其实就是唤醒消费者进程 cond.release()#解锁资源 time.sleep(self.sleeptime) class Consumer(threading.Thread):#消费者类 def __init__(self,condition,goods,sleeptime = 2):#sleeptime=2 threading.Thread.__init__(self) self.cond = condition self.goods = goods self.sleeptime = sleeptime def run(self): cond = self.cond goods = self.goods while True: time.sleep(self.sleeptime) cond.acquire()#锁住资源 while goods.empty():#如无产品则让线程等待 cond.wait() goods.sub() print("产品数量:",goods.count,"消费者线程") cond.release()#解锁资源 g = Goods() c = threading.Condition() pro = Producer(c,g) pro.start() con = Consumer(c,g) con.start()
Condition内部有一把锁,默认是RLock,在调用wait()和notify()之前必须先调用acquire()获取这个锁,才能继续执行;当wait()和notify()执行完后,需调用release()释放这个锁,在执行with condition时,会先执行acquire(),with结束时,执行了release();所以condition有两层锁,最底层锁在调用wait()时会释放,同时会加一把锁到等待队列,等待notify()唤醒释放锁
wait() :允许等待某个条件变量的通知,notify()可唤醒
notify(): 唤醒等待队列wait()
# encoding: UTF-8 import threading import time # 商品 product = None # 条件变量 con = threading.Condition() # 生产者方法 def produce(): global product if con.acquire(): while True: if product is None: print 'produce...' product = 'anything' # 通知消费者,商品已经生产 con.notify() # 等待通知 con.wait() time.sleep(2) # 消费者方法 def consume(): global product if con.acquire(): while True: if product is not None: print 'consume...' product = None # 通知生产者,商品已经没了 con.notify() # 等待通知 con.wait() time.sleep(2) t1 = threading.Thread(target=produce) t2 = threading.Thread(target=consume) t2.start() t1.start()
3.4 同步队列
put方法和task_done方法,queue有一个未完成任务数量num,put依次num+1,task依次num-1.任务都完成时任务结束。
import threading import queue import time import random ''' 1.创建一个 Queue.Queue() 的实例,然后使用数据对它进行填充。 2.将经过填充数据的实例传递给线程类,后者是通过继承 threading.Thread 的方式创建的。 3.每次从队列中取出一个项目,并使用该线程中的数据和 run 方法以执行相应的工作。 4.在完成这项工作之后,使用 queue.task_done() 函数向任务已经完成的队列发送一个信号。 5.对队列执行 join 操作,实际上意味着等到队列为空,再退出主程序。 ''' class jdThread(threading.Thread): def __init__(self,index,queue): threading.Thread.__init__(self) self.index = index self.queue = queue def run(self): while True: time.sleep(1) item = self.queue.get() if item is None: break print("序号:",self.index,"任务",item,"完成") self.queue.task_done()#task_done方法使得未完成的任务数量-1 q = queue.Queue(0) ''' 初始化函数接受一个数字来作为该队列的容量,如果传递的是 一个小于等于0的数,那么默认会认为该队列的容量是无限的. ''' for i in range(2): jdThread(i,q).start()#两个线程同时完成任务 for i in range(10): q.put(i)#put方法使得未完成的任务数量+1
3.5 Event对象
Event对象是一种简单的线程同步通信技术,一个线程设置Event对象,另一个线程等待Event对象。
import threading # 自定义线程类 class MyThread(threading.Thread): def __init__(self, thread_name): threading.Thread.__init__(self, name=thread_name) # 重写线程代码 def run(self): global my_event if my_event.isSet(): my_event.clear() # 等待通知 my_event.wait() print(self.getName()) else: print(self.getName()) my_event.set() # 创建锁 my_event = threading.Event() my_event.set() t1 = [] for i in range(10): t = MyThread(str(i)) t1.append(t) for t in t1: t.start()
-
-
java线程同步的实现方式
2019-03-08 01:47:21这里抛砖引玉,为何要使用同步?...下面总结一些java线程实现同步方式,大致有下面几种: 1.同步方法 使用 synchronized关键字,可以修饰普通方法、静态方法,以及语句块。由于java的每个对象都有一个内置锁...这里抛砖引玉,为何要使用同步?
当多个线程同时操作一个可共享的资源时会出现线程安全问题,将会导致数据不一致,因此使用同步锁来防止该操作执行完之前不许被其他线程执行,从而保证了该变量的唯一性和准确性。下面总结一些java线程实现同步方式,大致有下面几种:
1.同步方法
使用 synchronized关键字,可以修饰普通方法、静态方法,以及语句块。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。需要注意的是调用静态方法时,锁住的不是对象,锁住的是类。//修饰普通方法 public synchronized void add(){} //修饰语句块 synchronized(object){ } //修饰静态方法 public static synchronized int increase(){ }
同步是一种高开销的操作,因此应该尽量减少同步的内容。 通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
2.使用特殊域变量(volatile)实现线程同步- volatile关键字为域变量的访问提供了一种免锁机制,
- 使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
- 因此每次使用该域就要重新计算,而不是使用寄存器中的值
- volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
多线程中的非同步问题主要出现在对域的读写上,如果让域自身避免这个问题,则就不需要修改操作该域的方法。
用final域,有锁保护的域和volatile域可以避免非同步的问题。
3.使用重入锁实现线程同步在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。 ReentrantLock类是可重入、互斥、实现了Lock接口的锁,
它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
ReenreantLock类的常用方法有:- ReentrantLock() : 创建一个ReentrantLock实例
- lock() : 获得锁
- unlock() : 释放锁
ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用
关于Lock对象和synchronized关键字的选择:
a.最好两个都不用,使用一种java.util.concurrent包提供的机制,
能够帮助用户处理所有与锁相关的代码。
b.如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码
c.如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁
4.使用局部变量实现线程同步
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本, 副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。ThreadLocal 类的常用方法
- ThreadLocal() : 创建一个线程本地变量
- get() : 返回此线程局部变量的当前线程副本中的值
- initialValue() : 返回此线程局部变量的当前线程的"初始值"
- set(T value) : 将此线程局部变量的当前线程副本中的值设置为value
ThreadLocal与同步机制
a.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。
b.前者采用以"空间换时间"的方法,后者采用以"时间换空间"的方式5.使用阻塞队列实现线程同步
上面的实现方式都是在底层实现的线程同步,但是我们在实际开发当中,应当尽量远离底层结构。 使用javaSE5.0版本中新增的java.util.concurrent包将有助于简化开发。 例如使用LinkedBlockingQueue<E>来实现线程的同步 。
LinkedBlockingQueue<E>是一个基于已连接节点的,范围任意的blocking queue。
队列是先进先出的顺序(FIFO),关于队列以后会详细讲解~
LinkedBlockingQueue 类常用方法
LinkedBlockingQueue() : 创建一个容量为Integer.MAX_VALUE的LinkedBlockingQueue
put(E e) : 在队尾添加一个元素,如果队列满则阻塞
size() : 返回队列中的元素个数
take() : 移除并返回队头元素,如果队列空则阻塞BlockingQueue<E>定义了阻塞队列的常用方法,尤其是三种添加元素的方法,我们要多加注意,当队列满时:
- add()方法会抛出异常
- offer()方法返回false
- put()方法会阻塞
6.使用原子变量实现线程同步
原子操作就是指将读取变量值、修改变量值、保存变量值看成一个整体来操作,即-这几种行为要么同时完成,要么都不完成。在java的util.concurrent.atomic包中提供了创建了原子类型变量的工具类,使用该类可以简化线程同步。其中AtomicInteger 表可以用原子方式更新int的值,可用在应用程序中(如以原子方式增加的计数器),但不能用于替换Integer;可扩展Number,允许那些处理机遇数字类的工具和实用工具进行统一访问。
AtomicInteger类常用方法:- AtomicInteger(int initialValue) : 创建具有给定初始值的新的AtomicInteger
- addAddGet(int dalta) : 以原子方式将给定值与当前值相加
- get() : 获取当前值
代码实例:
1 class Bank { 2 private AtomicInteger account = new AtomicInteger(100); 3 4 public AtomicInteger getAccount() { 5 return account; 6 } 7 8 public void save(int money) { 9 account.addAndGet(money); 10 } 11 }
补充--原子操作主要有:
对于引用变量和大多数原始变量(long和double除外)的读写操作;
对于所有使用volatile修饰的变量(包括long和double)的读写操作。 -
C++多线程同步的几种方式
2019-12-26 20:25:35文章目录Overviewmutexlock_guardunique_lockcondition_variablefuturepromisepackaged_...C++的多线程同步方式有这么几种: mutex lock_guard unique_lock condition_variable future promise packaged...Overview
C++的多线程同步方式有这么几种:
mutex
lock_guard
unique_lock
condition_variable
future
promise
packaged_task
async
C++11并没有提供
semaphore
的API,信号量太容易出错了(too error prone),通过组合互斥锁(mutex)和条件变量(condition variable)可以达到相同的效果,且更加安全。mutex
官网介绍:mutex
它包含以下三个部分:- Mutex type
- Locks:
lock_guard
,unique_lock
- Functions:
try_lock
,lock
example:
mutex的实现也很简单,在进入临界区之前调用该变量(mtx
)的lock
函数,出临界区之前调用该变量的(mtx
)的unlock
函数。所以程序会连续输出50个'*'
或者连续输出50个'$'
,而不会'*'
'$'
交替输出。但是考虑这样一个问题,
std::cout << '\n'
,这里发生异常会发生什么?抛出异常后,意味着mtx.unlock()
不会被执行,即锁没有被释放,整个程序进入不了临界区,该程序往往会挂死。// mutex example #include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex std::mutex mtx; // mutex for critical section void print_block (int n, char c) { // critical section (exclusive access to std::cout signaled by locking mtx): mtx.lock(); for (int i=0; i<n; ++i) { std::cout << c; } std::cout << '\n'; mtx.unlock(); } int main () { std::thread th1 (print_block,50,'*'); std::thread th2 (print_block,50,'$'); th1.join(); th2.join(); return 0; }
Possible output (order of lines may vary, but characters are never mixed):
************************************************** $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
lock_guard
在构造时,互斥对象被调用线程锁定,而在销毁时,互斥对象被解锁。它是最简单的锁,作为具有自动持续时间的对象特别有用,该持续时间一直持续到其上下文结束。这样,可以保证在抛出异常的情况下互斥对象已正确解锁。
example
// lock_guard example #include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex, std::lock_guard #include <stdexcept> // std::logic_error std::mutex mtx; void print_even (int x) { if (x%2==0) std::cout << x << " is even\n"; else throw (std::logic_error("not even")); } void print_thread_id (int id) { try { // using a local lock_guard to lock mtx guarantees unlocking on destruction / exception: std::lock_guard<std::mutex> lck (mtx); print_even(id); } catch (std::logic_error&) { std::cout << "[exception caught]\n"; } } int main () { std::thread threads[10]; // spawn 10 threads: for (int i=0; i<10; ++i) threads[i] = std::thread(print_thread_id,i+1); for (auto& th : threads) th.join(); return 0; }
possible output
[exception caught] 2 is even [exception caught] 4 is even [exception caught] 6 is even [exception caught] 8 is even [exception caught] 10 is even
unique_lock
unique_lock基本用法和lock_guard一致,在构造函数和析构函数中进行锁操作,不同的地方在于它提供了非常多构造函数。
example
// unique_lock example #include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock std::mutex mtx; // mutex for critical section void print_block (int n, char c) { // critical section (exclusive access to std::cout signaled by lifetime of lck): std::unique_lock<std::mutex> lck (mtx); for (int i=0; i<n; ++i) { std::cout << c; } std::cout << '\n'; } int main () { std::thread th1 (print_block,50,'*'); std::thread th2 (print_block,50,'$'); th1.join(); th2.join(); return 0; }
condition_variable
条件变量是一个对象,可以阻塞线程,直到被通知恢复。当调用其等待功能之一时,它使用
unique_lock
(通过互斥锁)来锁定线程。该线程将保持阻塞状态,直到被另一个在同一个condition_variable
对象上调用通知功能的线程唤醒为止。
Wait functionswait
- Wait until notified (public member function )
wait_for
- Wait for timeout or until notified (public member function )
wait_until
- Wait until notified or time point (public member function )
Notify functions
notify_one
- Notify one (public member function )
notify_all
- Notify all (public member function )
example
// condition_variable::notify_one #include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock #include <condition_variable> // std::condition_variable std::mutex mtx; std::condition_variable produce,consume; int cargo = 0; // shared value by producers and consumers void consumer () { std::unique_lock<std::mutex> lck(mtx); while (cargo==0) consume.wait(lck); std::cout << cargo << '\n'; cargo=0; produce.notify_one(); } void producer (int id) { std::unique_lock<std::mutex> lck(mtx); while (cargo!=0) produce.wait(lck); cargo = id; consume.notify_one(); } int main () { std::thread consumers[10],producers[10]; // spawn 10 consumers and 10 producers: for (int i=0; i<10; ++i) { consumers[i] = std::thread(consumer); producers[i] = std::thread(producer,i+1); } // join them back: for (int i=0; i<10; ++i) { producers[i].join(); consumers[i].join(); } return 0; }
Possible output (order of consumed cargoes may vary):
1 2 3 4 5 6 7 8 9 10
future
future
的目标是充分利用CPU的并发性,它只能通过async
,promise
和package_task
三种方式构造。future
只能移动,不可复制,需要复制时可以使用shared_future
,但通常不建议使用。调用future
的get()时可能会发生阻塞,直到返回值ready。future
有三种姿势的等待:wait()
:一直等待直到得到返回值wait_for()
:设定一个超时时间;wait_until()
:等待到某个时间点。
future有一特化版本future,返回值为空,即不返回任何值,因此仅能用于线程间通知,但却是最常用的future。
promise
promise对象可以通过调用成员get_future将此共享状态与future对象关联。调用之后,两个对象共享相同的共享状态:
promise
:promise对象是异步提供者,在共享状态的时候设置一个值future
负责:future对象是异步返回对象,可以检索共享状态的值,并在必要时等待其准备就绪
example
// promise example #include <iostream> // std::cout #include <functional> // std::ref #include <thread> // std::thread #include <future> // std::promise, std::future void print_int (std::future<int>& fut) { int x = fut.get(); std::cout << "value: " << x << '\n'; } int main () { std::promise<int> prom; // create promise std::future<int> fut = prom.get_future(); // engagement with future std::thread th1 (print_int, std::ref(fut)); // send future to new thread prom.set_value (10); // fulfill promise // (synchronizes with getting the future) th1.join(); return 0; }
Output:
value: 10
packaged_task
很多情况下并不希望另起一个线程,因为线程是非常重要的资源。因此希望可以合理的管理线程资源,这就需要使用线程池。如何将future与线程池同时使用呢?这就需要采用package_task。package_task本质是将一个函数包装成一个future。这个task类似于std::function,有输入输出,大家可以将其认为是一个异步函数,但该异步函数并不负责执行,而是将其结果预置于一个future变量中,然后交给一个线程来实际执行,此时主线程便可以得到其返回值。
example
// packaged_task example #include <iostream> // std::cout #include <future> // std::packaged_task, std::future #include <chrono> // std::chrono::seconds #include <thread> // std::thread, std::this_thread::sleep_for // count down taking a second for each value: int countdown (int from, int to) { for (int i=from; i!=to; --i) { std::cout << i << '\n'; std::this_thread::sleep_for(std::chrono::seconds(1)); } std::cout << "Lift off!\n"; return from-to; } int main () { std::packaged_task<int(int,int)> tsk (countdown); // set up packaged_task std::future<int> ret = tsk.get_future(); // get future std::thread th (std::move(tsk),10,0); // spawn thread to count down from 10 to 0 // ... int value = ret.get(); // wait for the task to finish and get result std::cout << "The countdown lasted for " << value << " seconds.\n"; th.join(); return 0; }
Possible output:
10 9 8 7 6 5 4 3 2 1 Lift off! The countdown lasted for 10 seconds.
async
有时某项工作很早就可以开始做(前置条件都已完备),而等待这件工作结果的任务在非常靠后的位置,这时候就需要async。换言之,如果可以尽早开始做一件事,就让其在后台运行即可,或快或慢都可以,只需在需要结果的时候运行完成就好。
example
// async example #include <iostream> // std::cout #include <future> // std::async, std::future // a non-optimized way of checking for prime numbers: bool is_prime (int x) { std::cout << "Calculating. Please, wait...\n"; for (int i=2; i<x; ++i) if (x%i==0) return false; return true; } int main () { // call is_prime(313222313) asynchronously: std::future<bool> fut = std::async (is_prime,313222313); std::cout << "Checking whether 313222313 is prime.\n"; // ... bool ret = fut.get(); // waits for is_prime to return if (ret) std::cout << "It is prime!\n"; else std::cout << "It is not prime.\n"; return 0; }
Possible output (the first two lines may be in a different order, or scrambled):
Checking whether 313222313 is prime. Calculating. Please, wait... It is prime!
Reference
-
线程同步的四种方式
2019-10-15 14:05:26一、为什么要进行线程同步? 多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的... -
关于C语言中线程同步的方式
2020-07-31 10:48:49C语言中线程同步的方式线程同步互斥锁读写锁条件变量信号量 线程同步 在多线程环境中,线程之间由于竞争共享资源(临界资源)容易引起数据不一致的问题。一般采用互斥锁(互斥信号量)解决,保证只有一个线程进入... -
实现线程同步的几种方式总结
2018-07-25 21:31:26在这种情况下如何保证线程按照一定的顺序进行执行,今天就来一个大总结,分别介绍一下几种方式。 通过Object的wait和notify 通过Condition的awiat和signal 通过一个阻塞队列 通过两个阻塞队列 ... -
linux中实现线程同步的6种方法
2020-10-22 16:37:21linux线程同步的方法 下面是一个线程不安全的例子: #include<stdio.h> #include<pthread.h> int ticket_num=10000000; void *sell_ticket(void *arg) { while(ticket_num>0) { ticket_num--; }... -
线程同步常用方式与区别
2017-06-20 14:02:12在介绍线程同步/互斥之前,我们先要理解同步与互斥的概念,引用书上的解释来说明这2个概念: 1、线程(进程)同步的主要任务 在引入多线程后,由于线程执行的异步性,会给系统造成混乱,特别是在急用临界资源时,... -
线程同步的方式有哪些(简单易懂)
2020-05-13 14:20:56什么是线程同步 线程同步:这里的同步并非同时...线程同步的方式和机制 1、临界区(Critical Section):在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,有一个线程进入后,其他线程 -
C语言多线程编程-死锁和线程同步方式介绍(一)
2017-04-05 21:10:20线程同步,互斥锁,条件变量,读写锁 -
线程同步的几种方式
2017-07-29 14:24:49不同线程的执行时间会造成结果的不同,这时候就需要线程同步:线程同步的四种方式: 互斥量(mutex):引入互斥锁,得到锁的线程执行(读,修改,写)的操作,没有获得锁的线程只能等待,不能共享数据。(读,写,修改... -
Android-线程常用方法-线程同步
2019-02-16 15:45:52线程常用方法: 1.start():线程调用该方法将启动线程从新建状态进入就绪,一旦轮到享用CPU资源时,就开始自己的生命周期 2.run():Thread类的run()方法与Runnable接口的run()方法的功能和作用相同,都用来定义线程对象... -
Java经典面试题(其二)——Java线程同步方式和线程本地变量
2017-09-08 20:14:39Java线程同步方式和线程本地变量——Java经典面试题(其二)实现线程同步的几种方式1.为何要使用同步? Java允许多线程并发控制,当多个线程同时操作一个可共享资源变量时(如数据的增删改查),将会导致数据不... -
C++线程同步的几种方式
2018-05-28 16:32:001.临界区,通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。确保在某个时刻只有一个线程能访问数据。此时其他线程如想访问数据则会被挂起,直到当前线程离开临界区。临界区被释放后,其他... -
C#实现多线程同步并发操作
2011-07-28 10:23:52C#实现多线程同步并发操作,在线源码,供你下载学习 -
Linux C++多线程同步的四种方式(非常详细)
2019-07-15 17:42:00背景问题:在特定的应用场景下,多线程不进行同步会造成什么问题?通过多线程模拟多窗口售票为例:#include <iostream> #include<pthread.h> #include<stdio.h> #include<stdlib.h> #... -
多线程——多线程同步的三种实现方法
2018-12-01 23:56:31当使用多线程访问同一个资源的时候,非常容易出现线程安全的问题(例如,当多个线程同时对一个数据进行修改的时候,会导致某些线程对数据的修改丢失)。 因此,需要采用同步机制来解决这种问题。而Java主要提供了三... -
C++实现线程同步的几种方式
2018-04-22 14:50:44线程同步是指同一进程中的多个线程互相协调工作从而达到一致性。之所以需要线程同步,是因为多个线程同时对一个数据对象进行修改操作时,可能会对数据造成破坏,下面是多个线程同时修改同一数据造成破坏的例子: 1 #... -
Linux线程间同步 —— 5种方式简介
2017-12-29 15:01:11linux 线程间5种同步方式简介 -
C++ 操作系统进程中线程同步的四种方式
2017-04-22 09:20:52线程同步的定义: 同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。“同”字从字面上容易理解为一起动作 其实不是,“同”字应是指协同、协助、互相配合。如进程、线程同步,可理解为进程或... -
线程同步的方法有哪些?
2019-02-26 20:41:00一、线程同步和线程互斥 首先我们要清楚什么是线程同步和线程互斥: 1、线程同步:线程同步指的是多个线程之间协调同步,按照预定的先后次序进行运行,这种先后次序取决于要完成的特定任务,最基本的场景就是:A... -
java多线程三种方式区别,java多线程,线程同步方式,线程同步加锁的方法,wait与sleep区别
2015-11-21 19:21:51java多线程三种方式区别 1)实现Runnable接口 定义线程类,实现Runnable接口,重写其public void run(),将此类的对象当做Thread类的构造函数中的参数 所有子线程公用一套run中代码 2)继承Thread... -
C#的线程同步
2022-04-05 12:21:23使用线程有几个原因。假设从应用程序中进行网络调用需要一定的时间。我们不希望用户界面停止响应,让用户一直等待直到从服务器返回一个响应。用户可以同时执行其他一些操作,或者甚至取消发送给服务器的请求。这些都... -
C 线程同步的四种方式(Windows)
2020-02-21 18:08:31一、为什么要进行线程同步? 在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作。更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解。正常情况下对这种处理... -
多线程实现同步的七种方式
2017-11-15 19:43:55JAVA中线程同步的方法(7种)汇总 同步的方法: 一、同步方法 即有synchronized关键字修饰的方法。 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前... -
实现线程同步的几种方式
2016-05-19 13:06:13为何要使用同步?java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查)...同步的方式1.同步方法 即有synchronized关键字修饰的方法。 由于java的每个对象都有一个内置锁,当用此 -
Java synchronized关键字实现线程同步
2022-04-02 08:47:30synchronized关键字实现线程同步