当前位置:首页 » 编程语言 » mutexpython

mutexpython

发布时间: 2023-02-05 15:33:55

python 多线程 改变变量需要加锁么

python的锁可以独立提取出来

1
2
3
4
5
6
7
8

mutex = threading.Lock()
#锁的使用
#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire([timeout])
#释放
mutex.release()

概念
好几个人问我给资源加锁是怎么回事,其实并不是给资源加锁, 而是用锁去锁定资源,你可以定义多个锁, 像下面的代码, 当你需要独占某一资源时,任何一个锁都可以锁这个资源
就好比你用不同的锁都可以把相同的一个门锁住是一个道理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

import threading
import time

counter = 0
counter_lock = threading.Lock() #只是定义一个锁,并不是给资源加锁,你可以定义多个锁,像下两行代码,当你需要占用这个资源时,任何一个锁都可以锁这个资源
counter_lock2 = threading.Lock()
counter_lock3 = threading.Lock()

#可以使用上边三个锁的任何一个来锁定资源

class MyThread(threading.Thread):#使用类定义thread,继承threading.Thread
def __init__(self,name):
threading.Thread.__init__(self)
self.name = "Thread-" + str(name)
def run(self): #run函数必须实现
global counter,counter_lock #多线程是共享资源的,使用全局变量
time.sleep(1);
if counter_lock.acquire(): #当需要独占counter资源时,必须先锁定,这个锁可以是任意的一个锁,可以使用上边定义的3个锁中的任意一个
counter += 1
print "I am %s, set counter:%s" % (self.name,counter)
counter_lock.release() #使用完counter资源必须要将这个锁打开,让其他线程使用

if __name__ == "__main__":
for i in xrange(1,101):
my_thread = MyThread(i)
my_thread.start()

线程不安全:
最普通的一个多线程小例子。我一笔带过地讲一讲,我创建了一个继承Thread类的子类MyThread,作为我们的线程启动类。按照规定,重写Thread的run方法,我们的线程启动起来后会自动调用该方法。于是我首先创建了10个线程,并将其加入列表中。再使用一个for循环,开启每个线程。在使用一个for循环,调用join方法等待所有线程结束才退出主线程。
这段代码看似简单,但实际上隐藏着一个很大的问题,只是在这里没有体现出来。你真的以为我创建了10个线程,并按顺序调用了这10个线程,每个线程为n增加了1.实际上,有可能是A线程执行了n++,再C线程执行了n++,再B线程执行n++。
这里涉及到一个“锁”的问题,如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期(比如我们在每个线程的run方法中加入一个time.sleep(1),并同时输出线程名称,则我们会发现,输出会乱七八糟。因为可能我们的一个print语句只打印出一半的字符,这个线程就被暂停,执行另一个去了,所以我们看到的结果很乱),这种现象叫做“线程不安全”

线程锁:
于是,Threading模块为我们提供了一个类,Threading.Lock,锁。我们创建一个该类对象,在线程函数执行前,“抢占”该锁,执行完成后,“释放”该锁,则我们确保了每次只有一个线程占有该锁。这时候对一个公共的对象进行操作,则不会发生线程不安全的现象了。
于是,我们把代码更改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# coding : uft-8
__author__ = 'Phtih0n'
import threading, time
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global n, lock
time.sleep(1)
if lock.acquire():
print n , self.name
n += 1
lock.release()
if "__main__" == __name__:
n = 1
ThreadList = []
lock = threading.Lock()
for i in range(1, 200):
t = MyThread()
ThreadList.append(t)
for t in ThreadList:
t.start()
for t in ThreadList:
t.join()

1
2
3
4
5
6
7
8
9
10
11

1 Thread-2
2 Thread-3
3 Thread-4
4 Thread-6
5 Thread-7
6 Thread-1
7 Thread-8
8 Thread-9
9 Thread-5

Process finished with exit code 0


我们看到,我们先建立了一个threading.Lock类对象lock,在run方法里,我们使用lock.acquire()获得了这个锁。此时,其他的线程就无法再获得该锁了,他们就会阻塞在“if lock.acquire()”这里,直到锁被另一个线程释放:lock.release()。
所以,if语句中的内容就是一块完整的代码,不会再存在执行了一半就暂停去执行别的线程的情况。所以最后结果是整齐的。
就如同在java中,我们使用synchronized关键字修饰一个方法,目的一样,让某段代码被一个线程执行时,不会打断跳到另一个线程中。
这是多线程占用一个公共对象时候的情况。如果多个线程要调用多个现象,而A线程调用A锁占用了A对象,B线程调用了B锁占用了B对象,A线程不能调用B对象,B线程不能调用A对象,于是一直等待。这就造成了线程“死锁”。
Threading模块中,也有一个类,RLock,称之为可重入锁。该锁对象内部维护着一个Lock和一个counter对象。counter对象记录了acquire的次数,使得资源可以被多次require。最后,当所有RLock被release后,其他线程才能获取资源。在同一个线程中,RLock.acquire可以被多次调用,利用该特性,可以解决部分死锁问题。

⑵ python多线程全局变量和锁

1.python中数据类型,int,float,复数,字符,元组,做全局变量时需要在函数里面用global申明变量,才能对变量进行操作。

而,对象,列表,词典,不需要声明,直接就是全局的。

2.线程锁mutex=threading.Lock()

创建后就是全局的。线程调用函数可以直接在函数中使用。

mutex.acquire()开启锁

mutex=release()关闭锁

要注意,死锁的情况发生。

注意运行效率的变化:

正常1秒,完成56997921

加锁之后,1秒只运行了531187,相差10倍多。

3.继承.threading.Thread的类,无法调用__init__函数,无法在创建对象时初始化新建的属性。

4.线程在cpu的执行,有随机性

5. 新建线程时,需要传参数时,args是一个元组,如果只有一个参数,一定后面要加一个,符号。不能只有一个参数否则线程会报创建参数错误。threading.Thread(target=fuc,args=(arg,))

⑶ 深入解析Python中的线程同步方法

深入解析Python中的线程同步方法
同步访问共享资源
在使用线程的时候,一个很重要的问题是要避免多个线程对同一变量或其它资源的访问冲突。一旦你稍不留神,重叠访问、在多个线程中修改(共享资源)等这些操作会导致各种各样的问题;更严重的是,这些问题一般只会在比较极端(比如高并发、生产服务器、甚至在性能更好的硬件设备上)的情况下才会出现。
比如有这样一个情况:需要追踪对一事件处理的次数
counter = 0

def process_item(item):
global counter
... do something with item ...
counter += 1
如果你在多个线程中同时调用这个函数,你会发现counter的值不是那么准确。在大多数情况下它是对的,但有时它会比实际的少几个。
出现这种情况的原因是,计数增加操作实际上分三步执行:
解释器获取counter的当前值计算新值将计算的新值回写counter变量
考虑一下这种情况:在当前线程获取到counter值后,另一个线程抢占到了CPU,然后同样也获取到了counter值,并进一步将counter值重新计算并完成回写;之后时间片重新轮到当前线程(这里仅作标识区分,并非实际当前),此时当前线程获取到counter值还是原来的,完成后续两步操作后counter的值实际只加上1。
另一种常见情况是访问不完整或不一致状态。这类情况主要发生在一个线程正在初始化或更新数据时,另一个进程却尝试读取正在更改的数据。
原子操作
实现对共享变量或其它资源的同步访问最简单的方法是依靠解释器的原子操作。原子操作是在一步完成执行的操作,在这一步中其它线程无法获得该共享资源。
通常情况下,这种同步方法只对那些只由单个核心数据类型组成的共享资源有效,譬如,字符串变量、数字、列表或者字典等。下面是几个线程安全的操作:
读或者替换一个实例属性读或者替换一个全局变量从列表中获取一项元素原位修改一个列表(例如:使用append增加一个列表项)从字典中获取一项元素原位修改一个字典(例如:增加一个字典项、调用clear方法)
注意,上面提到过,对一个变量或者属性进行读操作,然后修改它,最终将其回写不是线程安全的。因为另外一个线程会在这个线程读完却没有修改或回写完成之前更改这个共享变量/属性。

锁是Python的threading模块提供的最基本的同步机制。在任一时刻,一个锁对象可能被一个线程获取,或者不被任何线程获取。如果一个线程尝试去获取一个已经被另一个线程获取到的锁对象,那么这个想要获取锁对象的线程只能暂时终止执行直到锁对象被另一个线程释放掉。
锁通常被用来实现对共享资源的同步访问。为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完后,再调用release方法释放锁:
lock = Lock()

lock.acquire() #: will block if lock is already held
... access shared resource
lock.release()

注意,即使在访问共享资源的过程中出错了也应该释放锁,可以用try-finally来达到这一目的:
lock.acquire()
try:
... access shared resource
finally:
lock.release() #: release lock, no matter what

在Python 2.5及以后的版本中,你可以使用with语句。在使用锁的时候,with语句会在进入语句块之前自动的获取到该锁对象,然后在语句块执行完成后自动释放掉锁:
from __future__ import with_statement #: 2.5 only

with lock:
... access shared resource

acquire方法带一个可选的等待标识,它可用于设定当有其它线程占有锁时是否阻塞。如果你将其值设为False,那么acquire方法将不再阻塞,只是如果该锁被占有时它会返回False:
if not lock.acquire(False):
... 锁资源失败
else:
try:
... access shared resource
finally:
lock.release()

你可以使用locked方法来检查一个锁对象是否已被获取,注意不能用该方法来判断调用acquire方法时是否会阻塞,因为在locked方法调用完成到下一条语句(比如acquire)执行之间该锁有可能被其它线程占有。
if not lock.locked():
#: 其它线程可能在下一条语句执行之前占有了该锁
lock.acquire() #: 可能会阻塞

简单锁的缺点
标准的锁对象并不关心当前是哪个线程占有了该锁;如果该锁已经被占有了,那么任何其它尝试获取该锁的线程都会被阻塞,即使是占有锁的这个线程。考虑一下下面这个例子:
lock = threading.Lock()

def get_first_part():
lock.acquire()
try:
... 从共享对象中获取第一部分数据
finally:
lock.release()
return data

def get_second_part():
lock.acquire()
try:
... 从共享对象中获取第二部分数据
finally:
lock.release()
return data

示例中,我们有一个共享资源,有两个分别取这个共享资源第一部分和第二部分的函数。两个访问函数都使用了锁来确保在获取数据时没有其它线程修改对应的共享数据。
现在,如果我们想添加第三个函数来获取两个部分的数据,我们将会陷入泥潭。一个简单的方法是依次调用这两个函数,然后返回结合的结果:

def get_both_parts():
first = get_first_part()
seconde = get_second_part()
return first, second

这里的问题是,如有某个线程在两个函数调用之间修改了共享资源,那么我们最终会得到不一致的数据。最明显的解决方法是在这个函数中也使用lock:
def get_both_parts():
lock.acquire()
try:
first = get_first_part()
seconde = get_second_part()
finally:
lock.release()
return first, second

然而,这是不可行的。里面的两个访问函数将会阻塞,因为外层语句已经占有了该锁。为了解决这个问题,你可以通过使用标记在访问函数中让外层语句释放锁,但这样容易失去控制并导致出错。幸运的是,threading模块包含了一个更加实用的锁实现:re-entrant锁。
Re-Entrant Locks (RLock)

RLock类是简单锁的另一个版本,它的特点在于,同一个锁对象只有在被其它的线程占有时尝试获取才会发生阻塞;而简单锁在同一个线程中同时只能被占有一次。如果当前线程已经占有了某个RLock锁对象,那么当前线程仍能再次获取到该RLock锁对象。
lock = threading.Lock()
lock.acquire()
lock.acquire() #: 这里将会阻塞

lock = threading.RLock()
lock.acquire()
lock.acquire() #: 这里不会发生阻塞

RLock的主要作用是解决嵌套访问共享资源的问题,就像前面描述的示例。要想解决前面示例中的问题,我们只需要将Lock换为RLock对象,这样嵌套调用也会OK.
lock = threading.RLock()

def get_first_part():
... see above

def get_second_part():
... see above

def get_both_parts():
... see above

这样既可以单独访问两部分数据也可以一次访问两部分数据而不会被锁阻塞或者获得不一致的数据。
注意RLock会追踪递归层级,因此记得在acquire后进行release操作。
Semaphores

信号量是一个更高级的锁机制。信号量内部有一个计数器而不像锁对象内部有锁标识,而且只有当占用信号量的线程数超过信号量时线程才阻塞。这允许了多个线程可以同时访问相同的代码区。
semaphore = threading.BoundedSemaphore()
semaphore.acquire() #: counter减小

... 访问共享资源
semaphore.release() #: counter增大

当信号量被获取的时候,计数器减小;当信号量被释放的时候,计数器增大。当获取信号量的时候,如果计数器值为0,则该进程将阻塞。当某一信号量被释放,counter值增加为1时,被阻塞的线程(如果有的话)中会有一个得以继续运行。
信号量通常被用来限制对容量有限的资源的访问,比如一个网络连接或者数据库服务器。在这类场景中,只需要将计数器初始化为最大值,信号量的实现将为你完成剩下的事情。
max_connections = 10

semaphore = threading.BoundedSemaphore(max_connections)

如果你不传任何初始化参数,计数器的值会被初始化为1.
Python的threading模块提供了两种信号量实现。Semaphore类提供了一个无限大小的信号量,你可以调用release任意次来增大计数器的值。为了避免错误出现,最好使用BoundedSemaphore类,这样当你调用release的次数大于acquire次数时程序会出错提醒。
线程同步

锁可以用在线程间的同步上。threading模块包含了一些用于线程间同步的类。
Events

一个事件是一个简单的同步对象,事件表示为一个内部标识(internal flag),线程等待这个标识被其它线程设定,或者自己设定、清除这个标识。
event = threading.Event()

#: 一个客户端线程等待flag被设定
event.wait()

#: 服务端线程设置或者清除flag
event.set()
event.clear()

一旦标识被设定,wait方法就不做任何处理(不会阻塞),当标识被清除时,wait将被阻塞直至其被重新设定。任意数量的线程可能会等待同一个事件。
Conditions

条件是事件对象的高级版本。条件表现为程序中的某种状态改变,线程可以等待给定条件或者条件发生的信号。
下面是一个简单的生产者/消费者实例。首先你需要创建一个条件对象:

#: 表示一个资源的附属项
condition = threading.Condition()
生产者线程在通知消费者线程有新生成资源之前需要获得条件:
#: 生产者线程
... 生产资源项
condition.acquire()
... 将资源项添加到资源中
condition.notify() #: 发出有可用资源的信号
condition.release()
消费者必须获取条件(以及相关联的锁),然后尝试从资源中获取资源项:
#: 消费者线程
condition.acquire()
while True:
...从资源中获取资源项
if item:
break
condition.wait() #: 休眠,直至有新的资源
condition.release()
... 处理资源

wait方法释放了锁,然后将当前线程阻塞,直到有其它线程调用了同一条件对象的notify或者notifyAll方法,然后又重新拿到锁。如果同时有多个线程在等待,那么notify方法只会唤醒其中的一个线程,而notifyAll则会唤醒全部线程。
为了避免在wait方法处阻塞,你可以传入一个超时参数,一个以秒为单位的浮点数。如果设置了超时参数,wait将会在指定时间返回,即使notify没被调用。一旦使用了超时,你必须检查资源来确定发生了什么。
注意,条件对象关联着一个锁,你必须在访问条件之前获取这个锁;同样的,你必须在完成对条件的访问时释放这个锁。在生产代码中,你应该使用try-finally或者with.
可以通过将锁对象作为条件构造函数的参数来让条件关联一个已经存在的锁,这可以实现多个条件公用一个资源:
lock = threading.RLock()
condition_1 = threading.Condition(lock)
condition_2 = threading.Condition(lock)

互斥锁同步
我们先来看一个例子:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time, threading

# 假定这是你的银行存款:
balance = 0
muxlock = threading.Lock()

def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n

def run_thread(n):
# 循环次数一旦多起来,最后的数字就变成非0
for i in range(100000):
change_it(n)

t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t3 = threading.Thread(target=run_thread, args=(9,))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print balance

结果 :

[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
61
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
24

上面的例子引出了多线程编程的最常见问题:数据共享。当多个线程都修改某一个共享数据的时候,需要进行同步控制。
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

threading模块中定义了Lock类,可以方便的处理锁定:
#创建锁mutex = threading.Lock()
#锁定mutex.acquire([timeout])
#释放mutex.release()

其中,锁定方法acquire可以有一个超时时间的可选参数timeout。如果设定了timeout,则在超时后通过返回值可以判断是否得到了锁,从而可以进行一些其他的处理。
使用互斥锁实现上面的例子的代码如下:
balance = 0
muxlock = threading.Lock()

def change_it(n):
# 获取锁,确保只有一个线程操作这个数
muxlock.acquire()
global balance
balance = balance + n
balance = balance - n
# 释放锁,给其他被阻塞的线程继续操作
muxlock.release()

def run_thread(n):
for i in range(10000):
change_it(n)

加锁后的结果,就能确保数据正确:
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0

⑷ python 程序如何防止生成的EXE重复执行

需要修改python代码
用lock 示例代码(网上搜的)
from threading import Thread, Lock

mutex = Lock()def processData(data):
mutex.acquire() try: print('Do some stuff') finally:
mutex.release()while True:
t = Thread(target = processData, args = (some_data,))
t.start()

⑸ python怎么让进程暂停

您的意思是要将进程挂起(Suspend) 而非 阻塞(Block)
如果用sleep() 进程将阻塞
假设进程下有两个线程 那么这两个线程会继续运行
要使进程挂起 可以考虑使用psutil
import psutil
p = psutil.Process(pid)
p.suspend() #挂起进程
p.resume() #恢复进程

为了证明效果 我写了一个简单的进程Process
其下有两个线程 读者Reader 和 写者Writer(简单的读者写者问题)

Process:
import threading

from time import ctime, sleep
import ThreadInReadAndWriteProblem
import multiprocessing
import os

class Process(multiprocessing.Process):

def __init__(self):
multiprocessing.Process.__init__(self) #手动实现父类
pid = os.getpid()

def run(self):
print '当前运行进程PID : %s ' %self.pid #子线程的id与父进程的pid相同 属于 同一个进程

for i in range(0,5):
r = ThreadInReadAndWriteProblem.Reader()
w = ThreadInReadAndWriteProblem.Writer()
w.start()
r.start()

print '进程阻塞'
sleep(10) #总共运行时间10秒

Reader&Writer
import threading
from time import ctime, sleep
import os

mutex = threading.Lock() #互斥锁
mutex_readercount = threading.Lock() #计数时的互斥 计算当前正在读的数目
readerCount = 0 number = 0

#不满足条件的 进入阻塞状态

class Reader(threading.Thread): #读者
def __init__(self):
threading.Thread.__init__(self) #继承父类构造函数

def run(self):
global mutex
global readerCount
#print '线程PID: %s ' %os.getpid()
while True:
mutex_readercount.acquire()
readerCount +=1
if readerCount == 1:
print '读者进程等待中,编号%s' %(self.name)
mutex.acquire() == False # 第一个需要申请

mutex_readercount.release()
print '开始读 , 读者编号 %s ,现在时间是 %s' %(self.name,ctime())
sleep(2)
print '完成读 , 读者编号 %s , 现在时间是 %s' %(self.name,ctime())

mutex_readercount.acquire()
readerCount -= 1
if readerCount == 0: #所有读者均完成
print '最后一个读者完成读 '
mutex.release()
mutex_readercount.release()

class Writer(threading.Thread): #写者
def __init__(self):
threading.Thread.__init__(self)

def run(self):
global mutex
global writerCount
#print '线程PID: %s' %os.getpid()
while True:
print '写者进程等待中 编号: %s' %(self.name)
mutex.acquire()
print '开始写 编号:%s 现在时间是: %s ' %(self.name,ctime())
sleep(5)
print '结束写 编号: %s 现在时间是 %s' %(self.name,ctime())
mutex.release()

测试程序
import ThreadInReadAndWriteProblem
import
import psutil
import Scheler
from time import ctime, sleep

def main():
p = .Process()
p.start()

sleep(3)

stop(p.pid)
print '进程挂起 %s' %ctime()
sleep(5)

wake(p.pid)
print '唤醒进程 %s' %ctime()

def stop(pid):
print '进程暂停 进程编号 %s ' %(pid)
p = psutil.Process(pid)
p.suspend()

def wake(pid):
print '进程恢复 进程编号 %s ' %(pid)
p = psutil.Process(pid)
p.resume()

if __name__ == '__main__':
main()

结果:
当前运行进程PID : 3096
写者进程等待中 编号: Thread-2
开始写 编号:Thread-2 现在时间是: Mon Nov 30 21:12:12 2015
读者进程等待中,编号Thread-1
写者进程等待中 编号: Thread-4
进程阻塞
写者进程等待中 编号: Thread-6
写者进程等待中 编号: Thread-8
写者进程等待中 编号: Thread-10
进程暂停 进程编号 3096
进程挂起 Mon Nov 30 21:12:15 2015
进程恢复 进程编号 3096
唤醒进程 Mon Nov 30 21:12:20 2015
结束写 编号: Thread-2 现在时间是 Mon Nov 30 21:12:20 2015
写者进程等待中 编号: Thread-2
开始读 , 读者编号 Thread-1 ,现在时间是 Mon Nov 30 21:12:20 2015

开始读 , 读者编号 Thread-3 ,现在时间是 Mon Nov 30 21:12:20 2015
开始读 , 读者编号 Thread-5 ,现在时间是 Mon Nov 30 21:12:20 2015
开始读 , 读者编号 Thread-7 ,现在时间是 Mon Nov 30 21:12:20 2015
开始读 , 读者编号 Thread-9 ,现在时间是 Mon Nov 30 21:12:20 2015
完成读 , 读者编号 Thread-1 , 现在时间是 Mon Nov 30 21:12:22 2015
完成读 , 读者编号 Thread-3 , 现在时间是 Mon Nov 30 21:12:22 2015
完成读 , 读者编号 Thread-5 , 现在时间是 Mon Nov 30 21:12:22 2015
完成读 , 读者编号 Thread-7 , 现在时间是 Mon Nov 30 21:12:22 2015

⑹ Python基础:全局解释器所GIL,

首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL
那么CPython实现中的GIL又是什么呢?GIL全称Global Interpreter Lock为了避免误导,我们还是来看一下官方给出的解释:
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
好吧,是不是看上去很糟糕?一个防止多线程并发执行机器码的一个Mutex,乍一看就是个BUG般存在的全局锁嘛!别急,我们下面慢慢的分析。
为什么会有GIL
由于物理上得限制,各CPU厂商在核心频率上的比赛已经被多核所取代。为了更有效的利用多核处理器的性能,就出现了多线程的编程方式,而随之带来的就是线程间数据一致性和状态同步的困难。即使在CPU内部的Cache也不例外,为了有效解决多份缓存之间的数据同步时各厂商花费了不少心思,也不可避免的带来了一定的性能损失。
Python当然也逃不开,为了利用多核,Python开始支持多线程。而解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁。 于是有了GIL这把超级大锁,而当越来越多的代码库开发者接受了这种设定后,他们开始大量依赖这种特性(即默认python内部对象是thread-safe的,无需在实现时考虑额外的内存锁和同步操作)。
慢慢的这种实现方式被发现是蛋疼且低效的。但当大家试图去拆分和去除GIL的时候,发现大量库代码开发者已经重度依赖GIL而非常难以去除了。有多难?做个类比,像MySQL这样的“小项目”为了把Buffer Pool Mutex这把大锁拆分成各个小锁也花了从5.5到5.6再到5.7多个大版为期近5年的时间,并且仍在继续。MySQL这个背后有公司支持且有固定开发团队的产品走的如此艰难,那又更何况Python这样核心开发和代码贡献者高度社区化的团队呢?
所以简单的说GIL的存在更多的是历史原因。如果推到重来,多线程的问题依然还是要面对,但是至少会比目前GIL这种方式会更优雅。
GIL的影响
从上文的介绍和官方的定义来看,GIL无疑就是一把全局排他锁。毫无疑问全局锁的存在会对多线程的效率有不小影响。甚至就几乎等于Python是个单线程的程序。 那么读者就会说了,全局锁只要释放的勤快效率也不会差啊。只要在进行耗时的IO操作的时候,能释放GIL,这样也还是可以提升运行效率的嘛。或者说再差也不会比单线程的效率差吧。理论上是这样,而实际上呢?Python比你想的更糟。
下面我们就对比下Python在多线程和单线程下得效率对比。测试方法很简单,一个循环1亿次的计数器函数。一个通过单线程执行两次,一个多线程执行。最后比较执行总时间。测试环境为双核的Mac pro。注:为了减少线程库本身性能损耗对测试结果带来的影响,这里单线程的代码同样使用了线程。只是顺序的执行两次,模拟单线程。

⑺ python mutex怎么释放

python 怎么在循环中释放内存 #include"stdio.h" main() { char st[15]; printf("input string:\n"); gets(st); puts(st); } 可以看出当输入的字符串中含有空格时,输出仍为全部字符串。说明gets函数并不以空格作为字符串输入结束的标志

⑻ python线程怎么销毁

【Python】线程的创建、执行、互斥、同步、销毁
还是《【Java】利用synchronized(this)完成线程的临界区》(点击打开链接)、《【Linux】线程互斥》(点击打开链接)、《【C++】Windows线程的创建、执行、互斥、同步、销毁》(点击打开链接)中的设置多个线程对一个ticket进行自减操作,用来说明Python中多线程的运用,涉及的创建、执行、互斥、同步、销毁问题。
运行结果如下,还是差不多,运行三次,每次的运行结果,每个线程最终的得票结果是不同的,但是4个线程最终“得票”的总和为 ticket 最初设置的值为100000,证明这4个线程成功实现了互斥。
虽然每次运行结果是不同,但是可以看得出每次运行结果大抵上是平均的。貌似Python对线程作系统资源的处理,比Java要好。
然而,Python总要实现多线程,代码并不像想象中简单,具体如下:
[python] view plain print?在CODE上查看代码片派生到我的代码片
# -*-coding:utf-8-*-
import threading;
mutex_lock = threading.RLock(); # 互斥锁的声明
ticket = 100000; # 总票数
# 用于统计各个线程的得票数
ticket_for_thread1 = 0;
ticket_for_thread2 = 0;
ticket_for_thread3 = 0;
ticket_for_thread4 = 0;
class myThread(threading.Thread): # 线程处理函数
def __init__(self, name):
threading.Thread.__init__(self); # 线程类必须的初始化
self.thread_name = name; # 将传递过来的name构造到类中的name
def run(self):
# 声明在类中使用全局变量
global mutex_lock;
global ticket;
global ticket_for_thread1;
global ticket_for_thread2;
global ticket_for_thread3;
global ticket_for_thread4;
while 1:
mutex_lock.acquire(); # 临界区开始,互斥的开始
# 仅能有一个线程↓↓↓↓↓↓↓↓↓↓↓↓
if ticket > 0:
ticket -= 1;
# 统计哪到线程拿到票
print "%s抢到了票!票还剩余:%d。" % (self.thread_name, ticket);
if self.thread_name == "线程1":
ticket_for_thread1 += 1;
elif self.thread_name == "线程2":
ticket_for_thread2 += 1;
elif self.thread_name == "线程3":
ticket_for_thread3 += 1;
elif self.thread_name == "线程4":
ticket_for_thread4 += 1;
else:
break;
# 仅能有一个线程↑↑↑↑↑↑↑↑↑↑↑↑
mutex_lock.release(); # 临界区结束,互斥的结束
mutex_lock.release(); # python在线程死亡的时候,不会清理已存在在线程函数的互斥锁,必须程序猿自己主动清理
print "%s被销毁了!" % (self.thread_name);
# 初始化线程
thread1 = myThread("线程1");
thread2 = myThread("线程2");
thread3 = myThread("线程3");
thread4 = myThread("线程4");
# 开启线程
thread1.start();
thread2.start();
thread3.start();
thread4.start();
# 等到线程1、2、3、4结束才进行以下的代码(同步)
thread1.join();
thread2.join();
thread3.join();
thread4.join();
print "票都抢光了,大家都散了吧!";
print "=========得票统计=========";
print "线程1:%d张" % (ticket_for_thread1);
print "线程2:%d张" % (ticket_for_thread2);
print "线程3:%d张" % (ticket_for_thread3);
print "线程4:%d张" % (ticket_for_thread4);
1、从上面的代码可以看出,在Python2.7中要使用线程必须使用threading而不是古老的thread模块。
如果你像网上部分遗留依旧的文章一样,在Python2.7中使用thread来实现线程,至少在Eclipse的Pydev中会报错:sys.excepthook is missing,lost sys.stderr如下图所示:
所以必须使用现时Python建议使用的threading。
2、与其它编程语言类似,声明一个互斥锁,与一系列的得票数。之后,与Java同样地,Python实现线程的函数,是要重写一个类。而类中使用全局变量,则与同为脚本语言的php一样《【php】global的使用与php的全局变量》(点击打开链接),要用global才能使用这个全局变量,而不是C/C++可以直接使用。
3、需要注意的,Python需要在线程跑完class myThread(threading.Thread)这个类的def run(self)方法之前,必须自己手动清理互斥锁,它不会像其它编程语言那样,说线程跑完def run(self)方法,会自然而然地清理该线程被创建的互斥锁。如果没有最后一句手动清理互斥锁,则会造成死锁。
4、最后与其它编程语言一样了,利用线程的join方法可以等待这个线程跑完def run(self)方法中的所有代码,才执行之后的代码,实现同步。否则主函数中的代码,相当于与父线程。主函数开启的线程,相当于其子线程,互不影响的。

⑼ python 什么是全局解释器锁gil

什么是Python全局解释器锁(GIL)?
每个CPU在同一时间只能执行一个线程,那么其他的线程就必须等待该线程的全局解释器,使用权消失后才能使用全局解释器,即使多个线程直接不会相互影响在同一个进程下也只有一个线程使用CPU,这样的机制称为全局解释器锁(GIL)。GIL的设计简化了CPython的实现,使得对象模型包括关键的内建类型,如:字典等,都是隐含的,可以并发访问的,锁住全局解释器使得比较容易的实现对多线程的支持,但也损失了多处理器主机的并行计算能力。
Python全局解释器锁(GIL)是一种互斥锁或锁,仅允许一个线程持有Python解释器的控制权。
全局解释器锁的好处
1、避免了大量的加锁解锁的好处;
2、使数据更加安全,解决多线程间的数据完整性和状态同步。
全局解释器锁的劣势
多核处理器退化成单核处理器,只能并发不能并行。
Python全局解释器锁(GIL)的作用
多线程情况下必须存在资源的竞争,GIL是为了保证在解释器级别的线程唯一使用共享资源(cpu)。

⑽ python多线程更改临界资源的时候有必要加锁吗

mutex = threading.Lock()
#锁的使用
#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire([timeout])
#释放
mutex.release()

概念
好几个人问我给资源加锁是怎么回事,其实并不是给资源加锁, 而是用锁去锁定资源,你可以定义多个锁, 像下面的代码, 当你需要独占某一资源时,任何一个锁都可以锁这个资源
就好比你用不同的锁都可以把相同的一个门锁住是一个道理

热点内容
如何把域名指向到指定服务器ip 发布:2024-05-07 10:48:49 浏览:365
base64javaphp 发布:2024-05-07 10:30:07 浏览:849
抖音青少年模式的密码是哪里的 发布:2024-05-07 10:05:27 浏览:752
tmp文件怎么解压 发布:2024-05-07 09:59:49 浏览:939
安卓手机如何提升录歌音质 发布:2024-05-07 09:49:55 浏览:331
指法运算法 发布:2024-05-07 09:24:26 浏览:195
兜享花为什么服务器错误 发布:2024-05-07 09:12:55 浏览:126
西门子编程仿真软件 发布:2024-05-07 09:12:04 浏览:128
脚本举例 发布:2024-05-07 09:04:41 浏览:819
php经历 发布:2024-05-07 08:59:25 浏览:420