Python之并发编程-多线程
目录
一、threading模块介绍
二、使用说明
三、进一步介绍(守护线程,锁(互斥锁、递归锁),信号量,队列,event,condition,定时器)
1、守护线程
2、锁(互斥锁、递归锁)
3、信号量
4、队列
5、event
6、condition
7、定时器
一、threading模块介绍
multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍
官网链接:https://docs.python.org/3/library/threading.html?highlight=threading#
理论:http://www.cnblogs.com/linhaifeng/articles/7430082.html
二、使用说明
1、方法说明
class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
This constructor should always be called with keyword arguments. Arguments are: group should be None; reserved for future extension when a ThreadGroup class is implemented. target is the callable object to be invoked by the run() method. Defaults to None, meaning nothing is called. name is the thread name. By default, a unique name is constructed of the form “Thread-N” where N is a small decimal number. args is the argument tuple for the target invocation. Defaults to (). kwargs is a dictionary of keyword arguments for the target invocation. Defaults to {}. If not None, daemon explicitly sets whether the thread is daemonic. If None (the default), the daemonic property is inherited from the current thread. If the subclass overrides the constructor, it must make sure to invoke the base class constructor (Thread.__init__()) before doing anything else to the thread. Changed in version 3.3: Added the daemon argument. start()
Start the thread’s activity. It must be called at most once per thread object. It arranges for the object’s run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. run()
Method representing the thread’s activity. You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively. join(timeout=None)
Wait until the thread terminates. This blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception – or until the optional timeout occurs. When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, you must call is_alive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out. When the timeout argument is not present or None, the operation will block until the thread terminates. A thread can be join()ed many times. join() raises a RuntimeError if an attempt is made to join the current thread as that would cause a deadlock. It is also an error to join() a thread before it has been started and attempts to do so raise the same exception. name
A string used for identification purposes only. It has no semantics. Multiple threads may be given the same name. The initial name is set by the constructor. getName()
setName()
Old getter/setter API for name; use it directly as a property instead. ident
The ‘thread identifier’ of this thread or None if the thread has not been started. This is a nonzero integer. See the get_ident() function. Thread identifiers may be recycled when a thread exits and another thread is created. The identifier is available even after the thread has exited. is_alive()
Return whether the thread is alive. This method returns True just before the run() method starts until just after the run() method terminates. The module function enumerate() returns a list of all alive threads. daemon
A boolean value indicating whether this thread is a daemon thread (True) or not (False). This must be set before start() is called, otherwise RuntimeError is raised. Its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False. The entire Python program exits when no alive non-daemon threads are left. isDaemon()
setDaemon()
Old getter/setter API for daemon; use it directly as a property instead.
Thread实例对象的方法
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。 threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
2、示例
(1)开启线程的2个方法
#方式一
from threading import Thread
import time
def talk(name):
time.sleep(2)
print('%s is talking' %name) if __name__ == '__main__':
t=Thread(target=talk,args=('a',))
t.start()
print('主线程') '''
主线程
a is talking
'''
方法一
#方式二
from threading import Thread
import time
class Talk(Thread):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
time.sleep(2)
print('%s is talking' % self.name) if __name__ == '__main__':
t = Talk('a')
t.start()
print('主线程') """
主线程
a is talking
"""
方法二
(2)进程和线程的一些对比
from threading import Thread
from multiprocessing import Process
import os def work():
print('hello') if __name__ == '__main__':
#在主进程下开启线程
t=Thread(target=work)
t.start()
print('主线程/主进程')
'''
打印结果:
hello
主线程/主进程
''' #在主进程下开启子进程
t=Process(target=work)
t.start()
print('主线程/主进程')
'''
打印结果:
主线程/主进程
hello
'''
打开子线程比打开子进程快
from threading import Thread
from multiprocessing import Process
import os def work():
print('hello',os.getpid()) if __name__ == '__main__':
#part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
t1=Thread(target=work)
t2=Thread(target=work)
t1.start()
t2.start()
print('主线程/主进程pid',os.getpid()) print("============================================")
#part2:开多个进程,每个进程都有不同的pid
p1=Process(target=work)
p2=Process(target=work)
p1.start()
p2.start()
print('主线程/主进程pid',os.getpid()) # 子线程存活在父进程里,pid一致
"""
hello 8116
hello 8116
主线程/主进程pid 8116
"""
# 子进程与父进程pid不一致,另开辟一块内存开启子进程
"""
============================================
主线程/主进程pid 8116
hello 9908
hello 764
"""
子线程、子线程的pid对比
# from threading import Thread
# from multiprocessing import Process
# import os
#
# def work():
# print('hello')
#
# if __name__ == '__main__':
# #在主进程下开启线程
# t=Thread(target=work)
# t.start()
# print('主线程/主进程')
# '''
# 打印结果:
# hello
# 主线程/主进程
# '''
#
# #在主进程下开启子进程
# t=Process(target=work)
# t.start()
# print('主线程/主进程')
# '''
# 打印结果:
# 主线程/主进程
# hello
# ''' #
# from threading import Thread
# from multiprocessing import Process
# import os
#
# def work():
# print('hello',os.getpid())
#
# if __name__ == '__main__':
# #part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
# t1=Thread(target=work)
# t2=Thread(target=work)
# t1.start()
# t2.start()
# print('主线程/主进程pid',os.getpid())
#
# print("============================================")
# #part2:开多个进程,每个进程都有不同的pid
# p1=Process(target=work)
# p2=Process(target=work)
# p1.start()
# p2.start()
# print('主线程/主进程pid',os.getpid())
#
# # 子线程存活在父进程里,pid一致
# """
# hello 8116
# hello 8116
# 主线程/主进程pid 8116
# """
# # 子进程与父进程pid不一致,另开辟一块内存开启子进程
# """
# ============================================
# 主线程/主进程pid 8116
# hello 9908
# hello 764
# """ from threading import Thread
from multiprocessing import Process
import os
def work_t():
global n
n=0
print("子线程里对象n的内存地址:",id(n)) def work_p():
global m
m=0
print("子进程里对象m的内存地址:",id(m)) if __name__ == '__main__':
n=100
print("父进程里对象n的内存地址:",id(n))
t=Thread(target=work_t)
t.start()
t.join()
print('父进程',n) #查看结果为0,因为同一进程内的线程之间共享进程内的数据 m = 1
print("父进程里对象n的内存地址:", id(m))
p=Process(target=work_p)
p.start()
p.join()
print('父进程',m) #毫无疑问子进程p已经将自己的全局的n改成了0,但改的仅仅是它自己的,查看父进程的n仍然为100 """
父进程里对象n的内存地址: 500620640
子线程里对象n的内存地址: 500619040
父进程 0 父进程里对象n的内存地址: 500619056
子进程里对象m的内存地址: 500619040
父进程 1 """
子线程使用父进程的对象,内存一致;进程新开辟内存,对象不一致
(3)join
与进程差不多,略
三、进一步介绍
1、守护线程
(1)说明
无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁
需要强调的是:运行完毕并非终止运行
#1.对主进程来说,运行完毕指的是主进程代码运行完毕
#1 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束, #2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
#2 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
(2)例子
from threading import Thread
import time
def talk(name):
time.sleep(10)
print('%s is talking' %name) if __name__ == '__main__':
t=Thread(target=talk,args=('a',))
t.setDaemon(True) #必须在t.start()之前设置
t.start() print('主线程')
print(t.is_alive())
'''
主线程
True # 立刻退出,不会sleep(10)
'''
daemon—1
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------- # 等待t2完成
end123
end456
"""
daemon-2
2、锁(互斥锁、递归锁)
(1)互斥锁Lock
用法
import threading R=threading.Lock() R.acquire()
'''
对公共数据的操作
'''
R.release()
示例
不加锁:并发执行,速度快,数据不安全
加锁:未加锁部分并发执行,加锁部分串行执行,速度慢,数据安全
串行join:整个线程串行,速度慢,数据安全
from threading import Thread,Lock
import os,time
def work(i):
global n
lock.acquire()
temp=n
time.sleep(0.1)
n=temp-1
lock.release()
print("线程{}结束".format(i)) if __name__ == '__main__':
lock=Lock()
n=100
l=[]
for i in range(100):
p=Thread(target=work,args=(i,))
l.append(p)
p.start()
for p in l:
p.join() print(n) #结果肯定为0,由原来的并发执行变成串行,牺牲了执行效率保证了数据安全
lock—1
分析:
#1.100个线程去抢GIL锁,即抢执行权限
#2. 肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire()
#3. 极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL
#4.直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程
GIL锁与互斥锁综合分析
(2)递归锁RLock
用法与Lock基本一致
from threading import Thread,Lock
import time
mutexA=Lock()
mutexB=Lock() class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutexA.acquire()
print('\033[41m%s 拿到A锁\033[0m' %self.name) mutexB.acquire()
print('\033[42m%s 拿到B锁\033[0m' %self.name)
mutexB.release() mutexA.release() def func2(self):
mutexB.acquire()
print('\033[43m%s 拿到B锁\033[0m' %self.name)
time.sleep(2) mutexA.acquire()
print('\033[44m%s 拿到A锁\033[0m' %self.name)
mutexA.release() mutexB.release() if __name__ == '__main__':
for i in range(10):
t=MyThread()
t.start() '''
Thread-1 拿到A锁
Thread-1 拿到B锁
Thread-1 拿到B锁
Thread-2 拿到A锁
然后就卡住,死锁了
'''
互斥锁的死锁现象
from threading import Thread,Lock
from threading import RLock
import time
# mutexA=Lock()
# mutexB=Lock()
mutexA = RLock()
mutexB = mutexA # 两者是同一个 class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutexA.acquire()
print('\033[41m%s 拿到A锁\033[0m' %self.name) mutexB.acquire()
print('\033[42m%s 拿到B锁\033[0m' %self.name)
mutexB.release() mutexA.release() def func2(self):
mutexB.acquire()
print('\033[43m%s 拿到B锁\033[0m' %self.name)
time.sleep(2) mutexA.acquire()
print('\033[44m%s 拿到A锁\033[0m' %self.name)
mutexA.release() mutexB.release() if __name__ == '__main__':
for i in range(10):
t=MyThread()
t.start() """
成功,结果略
"""
递归锁解决死锁问题
3、信号量
同进程的一样
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
与进程池是完全不同的概念,进程池Pool(4),最大只能产生4个进程,而且从头到尾都只是这四个进程,不会产生新的,而信号量是产生一堆线程/进程
from threading import Thread,Semaphore
import threading
import time def func():
sm.acquire()
print('%s get sm' %threading.current_thread().getName())
time.sleep(3)
sm.release()
if __name__ == '__main__':
sm=Semaphore(5)
for i in range(23):
t=Thread(target=func)
t.start()
信号量示例
4、队列
queue队列 :使用import queue,用法与进程Queue一样 class queue.Queue(maxsize=0) #先进先出 class queue.LifoQueue(maxsize=0) #last in fisrt out class queue.PriorityQueue(maxsize=0) #存储数据时可设置优先级的队列
Constructor for a priority queue. maxsize is an integer that sets the upperbound limit on the number of items that can be placed in the queue. Insertion will block once this size has been reached, until queue items are consumed. If maxsize is less than or equal to zero, the queue size is infinite. The lowest valued entries are retrieved first (the lowest valued entry is the one returned by sorted(list(entries))[0]). A typical pattern for entries is a tuple in the form: (priority_number, data). exception queue.Empty
Exception raised when non-blocking get() (or get_nowait()) is called on a Queue object which is empty. exception queue.Full
Exception raised when non-blocking put() (or put_nowait()) is called on a Queue object which is full. Queue.qsize()
Queue.empty() #return True if empty
Queue.full() # return True if full
Queue.put(item, block=True, timeout=None)
Put item into the queue. If optional args block is true and timeout is None (the default), block if necessary until a free slot is available. If timeout is a positive number, it blocks at most timeout seconds and raises the Full exception if no free slot was available within that time. Otherwise (block is false), put an item on the queue if a free slot is immediately available, else raise the Full exception (timeout is ignored in that case). Queue.put_nowait(item)
Equivalent to put(item, False). Queue.get(block=True, timeout=None)
Remove and return an item from the queue. If optional args block is true and timeout is None (the default), block if necessary until an item is available. If timeout is a positive number, it blocks at most timeout seconds and raises the Empty exception if no item was available within that time. Otherwise (block is false), return an item if one is immediately available, else raise the Empty exception (timeout is ignored in that case). Queue.get_nowait()
Equivalent to get(False). Two methods are offered to support tracking whether enqueued tasks have been fully processed by daemon consumer threads. Queue.task_done()
Indicate that a formerly enqueued task is complete. Used by queue consumer threads. For each get() used to fetch a task, a subsequent call to task_done() tells the queue that the processing on the task is complete. If a join() is currently blocking, it will resume when all items have been processed (meaning that a task_done() call was received for every item that had been put() into the queue). Raises a ValueError if called more times than there were items placed in the queue. Queue.join() block直到queue被消费完毕
queue方法
import queue q=queue.Queue()
q.put('first')
q.put('second')
q.put('third') print(q.get())
print(q.get())
print(q.get())
'''
结果(先进先出):
first
second
third
''' import queue q=queue.LifoQueue()
q.put('first')
q.put('second')
q.put('third') print(q.get())
print(q.get())
print(q.get())
'''
结果(后进先出):
third
second
first
''' import queue q=queue.PriorityQueue()
#put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c')) print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
(10, 'b')
(20, 'a')
(30, 'c')
'''
示例
5、event
说明
线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行
方法
event.isSet():返回event的状态值; event.wait():如果 event.isSet()==False将阻塞线程; event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; event.clear():恢复event的状态值为False。
示例
from threading import Thread,Event
import threading
import time,random
def conn_mysql():
count=1
while not event.is_set():
if count > 3:
raise TimeoutError('链接超时')
print('<%s>第%s次尝试链接' % (threading.current_thread().getName(), count))
event.wait(0.5)
count+=1
print('<%s>链接成功' %threading.current_thread().getName()) def check_mysql():
print('\033[45m[%s]正在检查mysql\033[0m' % threading.current_thread().getName())
time.sleep(random.randint(2,4))
event.set()
if __name__ == '__main__':
event=Event()
conn1=Thread(target=conn_mysql)
conn2=Thread(target=conn_mysql)
check=Thread(target=check_mysql) conn1.start()
conn2.start()
check.start()
先检查mysql状态,ok了再连接
6、condition
使得线程等待,只有满足某条件时,才释放n个线程
import threading def run(n):
con.acquire()
con.wait()
print("run the thread: %s" %n)
con.release() if __name__ == '__main__': con = threading.Condition()
for i in range(10):
t = threading.Thread(target=run, args=(i,))
t.start() while True:
inp = input('>>>')
if inp == 'q':
break
con.acquire()
con.notify(int(inp))
con.release()
示例1
def condition_func(): ret = False
inp = input('>>>')
if inp == '':
ret = True return ret def run(n):
con.acquire()
con.wait_for(condition_func)
print("run the thread: %s" %n)
con.release() if __name__ == '__main__': con = threading.Condition()
for i in range(10):
t = threading.Thread(target=run, args=(i,))
t.start()
示例2
7、定时器
定时器,指定n秒后执行某操作
from threading import Timer def hello():
print("hello, world") t = Timer(1, hello)
t.start() # after 1 seconds, "hello, world" will be printed
示例1
from threading import Timer
import random,time class Code:
def __init__(self):
self.make_cache() def make_cache(self,interval=5):
self.cache=self.make_code()
print(self.cache)
self.t=Timer(interval,self.make_cache)
self.t.start() def make_code(self,n=4):
res=''
for i in range(n):
s1=str(random.randint(0,9))
s2=chr(random.randint(65,90))
res+=random.choice([s1,s2])
return res def check(self):
while True:
inp=input('>>: ').strip()
if inp.upper() == self.cache:
print('验证成功',end='\n')
self.t.cancel()
break if __name__ == '__main__':
obj=Code()
obj.check()
示例2:验证码定时器
8、补充:threading.local
threading.local()这个方法的特点用来保存一个全局变量,但是这个全局变量只有在当前线程才能访问 localVal.val = name这条语句可以储存一个变量到当前线程,如果在另外一个线程里面再次对localVal.val进行赋值, 那么会在另外一个线程单独创建内存空间来存储,也就是说在不同的线程里面赋值 不会覆盖之前的值,因为每个 线程里面都有一个单独的空间来保存这个数据,而且这个数据是隔离的,其他线程无法访问 这个东西可以用在那些地方呢,比如下载,现在都是多线程下载了,就像酷狗那样,可以同时下载很多首歌曲,那么 就可以利用这个方法来保存每个下载线程的数据,比如下载进度,下载速度之类的 所以 如果你在开发多线程应用的时候 需要每个线程保存一个单独的数据供当前线程操作,可以考虑使用这个方法,简单有效 其实这样的功能还有很多种方法可以实现,比如我们在主线程实例化一个dict对象,然后用线程的名字作为key,因为线程之间可以共享数据, 所以也可以实现相同功能,并且灵活性更多,不过代码就没那么优雅简洁了
#coding=utf-8
import threading
# 创建全局ThreadLocal对象:
localVal = threading.local()
localVal.val = "Main-Thread"
def process_student():
print('%s (in %s)' % (localVal.val, threading.current_thread().name))
def process_thread(name):
#赋值
localVal.val = name
process_student()
t1 = threading.Thread(target= process_thread, args=('One',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Two',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
print(localVal.val) """
One (in Thread-A)
Two (in Thread-B)
Main-Thread
"""
示例1
# /usr/bin/env python
# coding:utf-8 __author__ = 'kikay' import threading # Threading.local对象
ThreadLocalHelper = threading.local()
lock = threading.RLock() class MyTheadEx(threading.Thread):
def __init__(self, threadName, name, age, sex):
super(MyTheadEx, self).__init__(name=threadName)
self.__name = name
self.__age = age
self.__sex = sex def run(self):
global ThreadLocalHelper
ThreadLocalHelper.ThreadName = self.name
ThreadLocalHelper.Name = self.__name
ThreadLocalHelper.Age = self.__age
ThreadLocalHelper.Sex = self.__sex
MyTheadEx.ThreadPoc() # 线程处理函数
@staticmethod
def ThreadPoc():
lock.acquire()
try:
print('Thread={id}'.format(id=ThreadLocalHelper.ThreadName))
print('Name={name}'.format(name=ThreadLocalHelper.Name))
print('Age={age}'.format(age=ThreadLocalHelper.Age))
print('Sex={sex}'.format(sex=ThreadLocalHelper.Sex))
print('----------')
finally:
lock.release() if __name__ == '__main__':
Tom = {'Name': 'tom', 'Age': 20, 'Sex': 'man'}
xiaohua = {'Name': 'xiaohua', 'Age': 18, 'Sex': 'woman'}
Andy = {'Name': 'Andy', 'Age': 40, 'Sex': 'man'}
T = (Tom, xiaohua, Andy)
threads = []
for i in range(len(T)):
t = MyTheadEx(threadName='id_{0}'.format(i), name=T[i]['Name'], age=T[i]['Age'], sex=T[i]['Sex'])
threads.append(t)
for i in range(len(threads)):
threads[i].start()
for i in range(len(threads)):
threads[i].join()
print('All Done!!!') """
Thread=id_0
Name=tom
Age=20
Sex=man
----------
Thread=id_1
Name=xiaohua
Age=18
Sex=woman
----------
Thread=id_2
Name=Andy
Age=40
Sex=man
----------
All Done!!!
"""
示例2
四、线程池
concurrent:http://www.cnblogs.com/fat39/p/8655040.html
参考或转发
http://www.cnblogs.com/linhaifeng/articles/7428877.html
Python之并发编程-多线程的更多相关文章
- python并发编程&多线程(二)
前导理论知识见:python并发编程&多线程(一) 一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性 官网链 ...
- python并发编程&多线程(一)
本篇理论居多,实际操作见: python并发编程&多线程(二) 一 什么是线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一 ...
- python week08 并发编程之多线程--理论部分
一. 什么是线程 1.定义 线程就像一条工厂车间里的流水线,一个车间里可以用很多流水线,来执行生产每个零部件的任务. 所以车间可以看作是进程,流水线可以看作是线程.(进程是资源单位,线程是执行单位) ...
- python 并发编程 多线程 目录
线程理论 python 并发编程 多线程 开启线程的两种方式 python 并发编程 多线程与多进程的区别 python 并发编程 多线程 Thread对象的其他属性或方法 python 并发编程 多 ...
- Python并发编程——多线程与协程
Pythpn并发编程--多线程与协程 目录 Pythpn并发编程--多线程与协程 1. 进程与线程 1.1 概念上 1.2 多进程与多线程--同时执行多个任务 2. 并发和并行 3. Python多线 ...
- python多进程并发和多线程并发和协程
为什么需要并发编程? 如果程序中包含I/O操作,程序会有很高的延迟,CPU会处于等待状态,这样会浪费系统资源,浪费时间 1.Python的并发编程分为多进程并发和多线程并发 多进程并发:运行多个独立的 ...
- Python 3 并发编程多进程之队列(推荐使用)
Python 3 并发编程多进程之队列(推荐使用) 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 可以往 ...
- [并发编程 - 多线程:信号量、死锁与递归锁、时间Event、定时器Timer、线程队列、GIL锁]
[并发编程 - 多线程:信号量.死锁与递归锁.时间Event.定时器Timer.线程队列.GIL锁] 信号量 信号量Semaphore:管理一个内置的计数器 每当调用acquire()时内置计数器-1 ...
- Python 3 并发编程多进程之进程同步(锁)
Python 3 并发编程多进程之进程同步(锁) 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,竞争带来的结果就是错乱,如何控制,就是加锁处理. 1. ...
随机推荐
- oracle 基本知识点
//创建临时表空间create temporary tablespace test_temp tempfile 'E:\oracle\product\10.2.0\oradata\testserver ...
- Mysql安装(win10 64位)
公司的测试数据库只有读的权限,而且还不能用IP和端口去访问,所有很多时候不方便(尤其是想练手的时候).闲着也是闲着,自己搭建一个Mysql数据库出来.以下操作,全部基于win10专业版 64位,仅供参 ...
- [转载]AVFormatContext结构体详细分析
AVFormatContext描述一个媒体文件或媒体流的构成和基本信息. 这是FFMpeg中最为基本的一个结构,是其他所有结构的根,是一个多媒体文件或流的根本抽象.其中nb_streams和s ...
- 【git3】git+Gogs+webStorm的使用
Gogs:相比gitlab更轻量级,基于golang的搭建自助 Git 服务.使用Gogs轻松搭建可能比GitLab更好用的Git服务平台. 参考资料:http://blog.csdn.net/u01 ...
- 小白第一次使用Git随笔
想研究Git很久了,一直没有找到很好的博客或论坛,近几天工作项目任务没有那么重,就想着找几篇文章把这玩意儿给解决掉,本博客是记录读廖雪峰老师所写的<Git教程>的随笔,以便巩固学习,若想学 ...
- Angular7教程-01-Angular开发环境配置
本教程基于angular7(2018-11-04) 1. 安装node.js 下载地址: http://nodejs.cn/download/ 下载对应自己操作系统的版本安装即可. 2.安装 angu ...
- python学习之路(1)
今天刚入门python,对于有c和java基础的我,学习起来还是比较容易的,我并没有用PyCharm写,而是最基础的IDLE,学习python比java容易的地方就是不要写分号,不要打包,不要定义等等 ...
- 针对于网络安全领域中基于PCAP流量的数据集
网络安全领域中基于PCAP流量的数据集 MAWI Working Group Traffic Archive URL:http://mawi.wide.ad.jp/mawi/ CIC dataset ...
- Firebird3基本使用
解决C#无法访问的情况:1. 使用FirebirdSql.Data.FirebirdClient 5版本以上.2.修改Firebird.conf配置文件WireCrypt为Enabled#WireCr ...
- 大数据入门第十一天——hive详解(一)入门与安装
一.基本概念 1.什么是hive The Apache Hive ™ data warehouse software facilitates reading, writing, and managin ...