前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >python多线程之间的同步(一)

python多线程之间的同步(一)

作者头像
py3study
发布于 2020-01-06 03:38:05
发布于 2020-01-06 03:38:05
53800
代码可运行
举报
文章被收录于专栏:python3python3
运行总次数:0
代码可运行

引言:

       线程之间经常需要协同工作,通过某种技术,让一个线程访问某些数据时,其它线程不能访问这些数据,直到该线程完成对数据的操作。这些技术包括临界区(Critical Section),互斥量(Mutex),信号量(Semaphore),事件Event等。

Event

threading库中的event对象通过使用内部一个flag标记,通过flag的True或者False的变化来进行操作。

名称

含义

set( )

标记设置为True

clear( )

标记设置为False

is_set( )

标记是否为True

wait(timeout=None)

设置等待标记为True的时长,None为无限等待。等到返回True,等不到返回False

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from  threading import Thread,Event
import time

def creditor(event:Event):
    print("什么时候还我钱")
    event.wait()
    print("我已经等了很长时间了")


def debtor(event:Event,count=10):
    print("可以宽裕几天吗?")
    money=[]
    while True:
        print("先还你100")
        time.sleep(0.5)
        money.append(1)
        if len(money)>count:
            event.set()
            break
    print("我已经还完你的钱了")

event=Event()
c=Thread(target=creditor,args=(event,))
d=Thread(target=debtor,args=(event,))
c.start()
d.start()

运行结果如下所示:

      可以看到creditor函数中因为event.wait( )线程进入等待状态,此时debtor线程进入运行,当满足条件时event.set( )将标记设置为True,creditor线程开始运行。谁wait就是等到flag变为True,或等到超时变为False。不限制等待的个数。

wait的使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from threading import Event,Thread


def Wait(event:Event,interval):
    while not event.wait(interval):
        print("waiting for you")

e=Event()
Thread(target=Wait,args=(e,3)).start()
e.wait(10)
e.set()
print("main exit")

主线程一开始就wait 10s,Waiting线程等待3s返回False,进入循环打印"waiting for you",重复3次,然后主线程set了,这时候Waiting线程变为True,不再进入循环。

Lock

     凡是存在资源争用的地方都可以使用锁,从而保证只有一个使用者可以完全使用这个资源

     现在要生产10个杯子,由10个工人开始生产

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import threading
import time 

cups=[]

def worker(count=10):
    print("我是{},我开始生产了".format(threading.current_thread().name))
    flag=False
    while True:
        if len(cups)>count:
            flag=True
        time.sleep(0.05)
        if not flag:
            cups.append(1)
        if flag:
            break 
    print("finished.cups={}".format(len(cups)))
    
for _  in range(10):
    threading.Thread(target=worker,args=(1000,)).start()

运行结果如下图所示:

      我们明明只需要到1000就会break,但是结果却到了1010个,这就是因为有10个线程,其中每个线程都在增加,但是增加后的数目,其他线程并不会知道(每个线程通过len函数拿到数量,但是刚拿到数字,其他线程就立即更新了)

      这个时候我们就需要锁lock来实现了,一旦线程获得锁,其他试图获取锁的线程将被阻塞

名称

含义

acquire(blocking=True,timeout=-1)

默认阻塞,阻塞可以设置超时时间。非阻塞时,timeout禁止设置。成功获取锁,返回True,否则返回False

release( )

释放锁。可以从任何线程释放。已上锁的锁,会抛出RuntimeError异常

加锁的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import  threading 
import  time 

cups=[]
lock=threading.Lock()

def worker(count=10):
    print("我是{},我开始生产了".format(threading.current_thread().name))
    flag=False
    while True:
        lock.acquire()
        if len(cups)>=count:
            flag=True
        time.sleep(0.005)
        if not flag:
            cups.append(1)
        lock.release()
        if flag:
            break
    print("finished,cups={}".format(len(cups)))
        
for _ in  range(10):
    threading.Thread(target=worker,args=(1000,)).start()

运行结果如图所示:

      一般来说加锁后还需要一些代码实现,在释放锁之前还有可能抛出异常,一旦出现异常,锁无法释放,但是当前这个线程会因为这个异常而终止,这样会产生死锁,因此使用时要使用如下的方法:

     1,使用try...finally语句保证锁的释放

     2,with安全上下文管理(锁对象支持上下文管理)

计数器类,用来加,减。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import threading
import time


class Counter:
    def __init__(self):
        self._val = 0
        self.__lock = threading.Lock()

    @property
    def value(self):
        return self._val

    def inc(self):
        try:
            self.__lock.acquire()
            self._val += 1
        finally:
            self.__lock.release()

    def dec(self):
        with self.__lock:
            self._val -= 1


def run(c: Counter, count=100):
    for _ in range(count):
        for i in range(-50, 50):
            if i < 0:
                c.dec()
            else:
                c.inc()


c = Counter()
c1 = 10
c2 = 1000


for i in range(c1):
    threading.Thread(target=run, args=(c, c2)).start()


while True:
    if threading.active_count() == 1:
        print(c.value)
        break

    启动了10个线程,1000次从-50到50进行加减,最后得到0,如果没有加锁处理的话,得到的结果未必是自己想要的。

锁的使用场景:

锁适用于访问和修改同一个资源的时候,引起资源争用的情况下。使用锁的注意事项:

        1,少用锁,除非有必要。多线程访问加锁的资源时,由于锁的存在,实际就变成了串行。

        2,加锁时间越短越好,不需要就立即释放锁。

        3,一定要避免死锁,使用with或者try...finally。

非阻塞锁使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import  threading
import  time


def worker(tasks):
    for task in tasks:
        time.sleep(0.001)
        if task.lock.acquire(False):
            print("{} {} begin to start".format(threading.current_thread(),task.name))
        else:
            print("{} {} is working".format(threading.current_thread(),task.name))


class Task:
    def __init__(self,name):
        self.name=name
        self.lock=threading.Lock()

tasks=[Task('task-{}'.format(x)) for x in range(10)]

for i in range(5):
    threading.Thread(target=worker,name="worker-{}".format(i),args=(tasks,)).start()

运行结果如下图所示:

       总共开启了5个线程,每个线程处理10个任务,因为在if语句里面,task.lock.acquire(False),所以每个线程只有拿到锁是True,其他的线程不会阻塞会返回False。打印"is working"。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/09/21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Python3 多线程
主流操作系统上完成并发的手段有进程和线程,主流的编程语言提供了用户空间的调度:协程。Python 也不例外。
py3study
2020/01/02
8400
python3--线程,锁,同步锁,递归锁,信号量,事件,条件和定时器,队列,线程池
这个99又赋值给n,进程变量就是99,所以每次都是赋值操作,赋值了100次,最终结果99,这样还是出现数据不安全的情况
py3study
2018/08/02
3.1K0
python基本 -- threading多线程模块的使用
python多线程编程,一般使用thread和threading模块。thread模块想对较底层,threading模块对thread模块进行了封装,更便于使用。所有,通常多线程编程使用threading模块。
lpe234
2020/07/27
4K0
Python自学成才之路 线程间协作 lock,condition,event的使用
多线程并发时会出现线程安全问题,如果不解决线程并发安全问题可能会让程序出现不可预料的情况。python提供了一些工具包来解决多线程安全问题,下面介绍其中常见的工具。
我是李超人
2020/09/08
1K0
py基础---多线程、多进程、协程
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程中并发执行不同的任务。
py3study
2020/01/16
6520
py基础---多线程、多进程、协程
python多线程爬虫
有些时候,比如下载图片,因为下载图片是一个耗时的操作。如果采用之前那种同步的方式下载。那效率肯会特别慢。这时候我们就可以考虑使用多线程的方式来下载图片。Pycharm激活注册码教程使用更多解释请见:https://vrg123.com/
用户2200417
2022/03/04
3570
Python之线程
操作系统线程理论 线程概念的引入背景 进程 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。正是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出
新人小试
2018/04/12
1.4K0
Python之线程
浅析Python多线程
学习Python多线程的资料很多,吐槽Python多线程的博客也不少。本文主要介绍Python多线程实际应用,且假设读者已经了解多线程的基本概念。如果读者对进程线程概念不甚了解,可参见知名博主 阮一峰 转译的一篇博客:《进程与线程的一个简单解释》。
用户1432189
2018/09/05
1.5K0
Python中线程同步与线程锁「建议收藏」
概念 * 线程同步,线程间协同,通过某种技术,让一个线程访问某些数据时,其他线程不能访问这些数据,直到该线程完 成对数据的操作。
全栈程序员站长
2022/08/31
7370
Python中线程同步与线程锁「建议收藏」
Python多线程
线程可以允许程序在同一进程空间中并发运行多个操作。本次主要介绍Python标准库中的多线程模块threading。
职场亮哥
2020/10/10
1.4K0
python多线程菜鸟教程_python实现多线程有几种方式
大家好,我是架构君,一个会写代码吟诗的架构师。今天说一说python多线程菜鸟教程_python实现多线程有几种方式,希望能够帮助大家进步!!!
Java架构师必看
2022/07/06
7480
python 实现线程之间的通信
前言:因为GIL的限制,python的线程是无法真正意义上并行的。相对于异步编程,其性能可以说不是一个等量级的。为什么我们还要学习多线程编程呢,虽然说异步编程好处多,但编程也较为复杂,逻辑不容易理解,学习成本和维护成本都比较高。毕竟我们大部分人还是适应同步编码的,除非一些需要高性能处理的地方采用异步。
kirin
2020/05/20
9850
Python多线程总结
在实际处理数据时,因系统内存有限,我们不可能一次把所有数据都导出进行操作,所以需要批量导出依次操作。为了加快运行,我们会采用多线程的方法进行数据处理,以下为我总结的多线程批量处理数据的模板:
用户8949263
2022/04/08
4460
Python 多线程 threadin
#!/usr/bin/python # -*- coding: utf-8 -*- __author__ = 'gaogd' ''' ### 多进程 import threading import time def run(num):     print 'Hi, I am thread %s..lalala' % num     time.sleep(1) for i in range(20):   t = threading.Thread(target=run, args=(i,))   t
py3study
2020/01/10
4580
python3 - 多线程 - thre
from threading import Thread import threading from multiprocessing import Process import os def work(): import time time.sleep(3) print(threading.current_thread().getName()) if __name__ == '__main__': #在主进程下开启线程 t=Thread(target=work) t.start() print(threading.current_thread().getName()) print(threading.current_thread()) #主线程 print(threading.enumerate()) #连同主线程在内有两个运行的线程 print(threading.active_count()) print('主线程/主进程') ''' 打印结果: MainThread <_MainThread(MainThread, started 140735268892672)> [<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>] 主线程/主进程 Thread-1 ''' 2-4 主进程等待新建线程实例 from threading import Thread import time def sayhi(name): time.sleep(2) print('%s say hello' %name) if __name__ == '__main__': t=Thread(target=sayhi,args=('egon',)) t.start() t.join() print('主进程') print(t.is_alive()) ''' >>>egon say hello >>>主进程 >>>False ''' 2-5 线程共用创造进程内资源 from threading import Thread n = 100 def task(): global n n = 0 if __name__ == '__main__': t = Thread(target=task) t.start() t.join() print('主', n) ''' >>>主 0 ''' 2-7 查看线程和‘父’进程的pid from threading import Thread import os def task(): print('%s is running' % os.getpid()) if __name__ == '__main__': t = Thread(target=task) t.start() print('主', os.getpid()) ''' >>>14488 is running >>>主 14488 ''' 2-8 Thread 实现 “守护线程” 2-8-1 守护线程概念介绍 守护线程:守护线程会等待主进程内,所有非守护线程,全部运行完毕后,才进行销毁。即,等待进程结束。 2-8-2 实现简单 ‘守护线程’ from threading import Thread import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") t1 = Thread(target=foo) t2 = Thread(target=bar) t1.daemon = True t1.start() t2.start() print("main-------") ''' >>>123 >>>456 >>>main------- >>>end123 >>>end456 ''' 2-9 Thread + Lock 实现线程“互斥锁”  from threading import Thread, Lock import time mutex = Lock() # 每个线程内创建一把锁 n
py3study
2020/01/13
4630
Python 进阶(一):多线程
Python 解释器的主要作用是将我们在 .py 文件中写好的代码交给机器去执行,比较常见的解释器包括如下几种:
Python小二
2020/08/18
4990
Python 中最常用的 5 种线程锁你会用吗?
对于日常开发者来讲很少会使用到本文的内容,但是对框架作者等是必备知识,同时也是高频的面试常见问题
杰哥的IT之旅
2021/11/10
2.3K0
一篇文章理清Python多线程之同步条件,信号量和队列
今天这篇文章大概介绍下python多线程中的同步条件Event,信号量(Semaphore)和队列(queue),这是我们多线程系列的最后一篇文章,以后将会进入python多进程的系列。
南山烟雨
2019/04/30
8500
一篇文章理清Python多线程之同步条件,信号量和队列
python中5种线程锁
线程安全是多线程或多进程编程中的一个概念,在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
Python学习者
2024/05/14
1060
4.并发编程多线程
multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍
changxin7
2019/09/10
7270
相关推荐
Python3 多线程
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文