python ==》 并发编程之多线程

一:什么是线程?

回答:在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程。线程:顾名思义,就是一个流水线工作的过程,一条流水线必须属于一个车间一个车间的工作过程就是一个线程,车间负责把资源整合到一起,就是一个资源单位,而一个车间内至少有一条流水线,那么流水线的工作需要电源,电源就相当于cpu。所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。

总结:简单的理解为: 进程就是一个车间, 线程就是这个车间里的流水线的工作流程。

多线程:在一个进程中存在多个线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都公用一个车间的资源。

二:线程的创建开销小:

创建进程的开销要远大于线程?

如果我们的软件是一个工厂,该工厂有多条流水线,流水线工作需要电源,电源只有一个即cpu(单核cpu)

一个车间就是一个进程,一个车间至少一条流水线(一个进程至少一个线程)

创建一个进程,就是创建一个车间(申请空间,在该空间内建至少一条流水线)

而建线程,就只是在一个车间内造一条流水线,无需申请空间,所以创建开销小

 

进程之间是竞争关系,线程之间是协作关系?

车间直接是竞争/抢电源的关系,竞争(不同的进程直接是竞争关系,是不同的程序员写的程序运行的,迅雷抢占其他进程的网速,360把其他进程当做病毒干死)
一个车间的不同流水线式协同工作的关系(同一个进程的线程之间是合作关系,是同一个程序写的程序内开启动,迅雷内的线程是合作关系,不会自己干自己)

三:线程与进程的区别:

1.线程的地址空间是可共享的,每个进程有自己的独立的地址空间。

2.一个进程中的线程直接接入他的进程的数据段,但是每个进程都有他们直接的从父进程拷贝过来的数据段

3.一个进程内部的线程之间能够直接通信,进程之间必须使用进程间通信实现通信

4.新的线程很容易被创建,新的进程需要从父进程复制

5.一个进程中的线程间能够有相当大的控制力度,进程仅仅只能控制他的子进程

6.改变主线程(删除,优先级改变等)可能影响这个进程中的其他线程,修改父进程不会影响子进程。

四:为何要用多线程

多线程指的是,在一个进程中开启多个线程,简单的讲:如果多个任务共用一块地址空间,那么必须在一个进程内开启多个线程。详细的讲分为4点:

  1. 多线程共享一个进程的地址空间

      2. 线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用

      3. 若多个线程都是cpu密集型的,那么并不能获得性能上的增强,但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠运行,从而会加快程序执行的速度。

      4. 在多cpu系统中,为了最大限度的利用多核,可以开启多个线程,比开进程开销要小的多。(这一条并不适用于python)

 

五:线程中相关的模块内容介绍。

1.threading 模块介绍:

multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍

2.开启线程的两种方式:

直接调用 And  继承调用

直接调用:

from threading import Thread  
import os 
def talk():
    print('%s is running'%os.getpid())

if __name__ == '__main__':
    t = Thread(target = talk)
    t.start()
    print('')

总结:Thread  开启线程,主程序后执行的。

输出结果:
8960 is running
主

Process finished with exit code 0


继承调用:

from threading import Thread
import os 
class Mythread(Thread):
    def __init__(self):
        super().__init__()    
        self.name= name
    def run(self):
        print('pid:%s name:[%s] is running'%(os.getpid(),self.name))

if __name__ =='__main__':
    t = Mythread('aray)
    t.start()
    print('',os.getpid())


输出结果:
pid:15648 name:[aray]is running 
主 15648

Process finished with exit code 0
线程的两种调用方式

3.在一个进程下开启多个线程与在一个进程下开启多个子进程的区别:

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
    '''
1.谁开的速度快
2.比较pid值
from  threading import Thread
from multiprocessing import Process
import os
def work():
    global n
    n=0

if __name__ == '__main__':
    # n=100
    # p=Process(target=work)
    # p.start()
    # p.join()
    # print('主',n) #毫无疑问子进程p已经将自己的全局的n改成了0,但改的仅仅是它自己的,查看父进程的n仍然为100


    n=1
    t=Thread(target=work)
    t.start()
    t.join()
    print('',n) #查看结果为0,因为同一进程内的线程之间共享进程内的数据
3 同一进程内的线程共享该进程的数据?

4.练习:模拟一个文编编辑器。就简单做一个大写化处理,并保存,写入文件。

from threading import Thread
import os 
msg_l = []
format_l = []

def Listen():
    '''监听输入'''
    while True:  #循环,不至于输入一次内容就关闭页面
        msg = input('>>:').strip()
        if not msg: continue
        msg_l.append(msg)   #把要输入的内容,添加到空列表里。

def Format():
    '''格式化输出'''
    while True:
        if msg_l:   #判断,只有msg_l 不为空,才往下执行. 就是要传数据上来。
            res = msg_l.pop()  #pop  队列输出
            format_l.append(res.upper())  #格式化之后的结果,添加到另一个新的空列表。

def Hold():
    ''' 保存'''
    while True:
        if format_l:  #判断,如果有个内容,为True,才往下执行。
            with open('db.txt','a',encoding = 'utf-8') as f:   
                res = format_l.pop()
                f.write('%s \n'res)

if __name__ == '__main__':
    t1 = Thread(target = Listen)    
    t2 = Thread(target = Format)
    t3 = Thread(target = Hold)

    t1.start()
    t2.start()    
    t3.start()


输出结果:
>>: a
>>: b
>>: c
这时,会把abc  以大写的形式,写入  db.txt 文件里面。
文本编辑器

5.线程的其他方法:

isAlive(): 返回线程是否活动的
getname(): 返回线程名
setname():设置线程名

threading.currentThread(): 返回当前的线程变量
threading.enumerate(): 返回一个包含正在运行的线程list,正在运行指线程启动后,结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与 len(threading.enumerate())有相同的结果。

from threading import Thread,currentThread,activeCount
import  os,time,threading

def talk():
    print('%s is running '%currentThread().getName())
    print(t.is_alive())

if __name__ == '__main__':
    t = Thread(target=talk,name='aray')
    t.start()
    time.sleep(1)
    print(t.is_alive())
    print(threading.activeCount())
    print(len(threading.enumerate()))
    print('',activeCount())

输出结果:
aray is running 
True
False
1
11

Process finished with exit code 0
举例

5.1:主线程等待机制:join

from threading import Tread,currentTread
import time

def talk():
    time.sleep(2)
    print('%s is running '%currentThread().getName())

if __name__ == '__main__':
    t1 = Thread(target = talk,name='aray')
    t1.start()
    print('主‘)

注意:没有join 之前。
输出结果:
主
aray is running 

Process finished with exit code 0

#加了join之后。
from threading import Tread,currentTread
import time

def talk():
    time.sleep(2)
    print('%s is running '%currentThread().getName())

if __name__ == '__main__':
    t1 = Thread(target = talk,name='aray')
    t1.start()
    t1.join()
    print('主‘)

输出结果:
aray is running 
主

Process finished with exit code 0
join

5.2:守护线程:daemon

from threading import Thread,currentThread,activeCount
from multiprocessing import  Process
import  os,time,threading

def talk():
    time.sleep(2)
    print('%s is running '%currentThread().getName())
    # print('%s is running '%os.getpid())
if __name__ == '__main__':
    t2 = Process(target = talk)
    t3 = Process(target = talk)
    t4 = Process(target = talk)
    t2.daemon = True
    t3.daemon = True
    t4.daemon = True
    t2.start()
    t3.start()
    t4.start()
    print('',os.getpid())


输出结果:
主 14576

Process finished with exit code 0
daemon

5.3:全局解释器锁

import time
from threading import Thread,Lock
n = 100
def work():
    mutex.acquire()
    global n
    temp = n
    time.sleep(0.5)
    n = temp -1
    mutex.release()

if __name__ =='__main__':
    mutex = Lock()
    t_l = []
    for i in range(100):
         t = Thread(target = work)
         t_l.append(t)
         t.start()
    for t in t_l:
        t.join()
    print(n)

输出结果:
0

Process finished with exit code 0
GIL锁

5.4:详解join

1.没有加锁
from threading import Thread,Lock
import time

n = 100
def work():
    global n
    temp = n
    time.sleep(0.1)
    n =temp -1
if __name__ == '__main__':
    mutex = Lock()
    start = time.time()
    for i in range(100):
        t = Thread(target=work)
        t.start()
        t.join()
        stop = time.time()
    print("%s  time: %s"%(n,stop-start))

输出结果:
0  time: 10.085442781448364

Process finished with exit code 0

2.加锁解决并发,竞争的问题。保证安全,但是所处理的时间也相应增加。
n = 100
def work():
    # time.sleep(2)
    global n
    mutex.acquire()
    temp = n
    time.sleep(0.2)
    n = temp -1
    mutex.release()
if __name__ == '__main__':
    mutex = Lock()
    l_t =[]
    start =time.time()
    for i in range(100):
        t = Thread(target=work)
        l_t.append(t)
        t.start()
    for t in l_t:
        t.join()
    stop = time.time()
    print("%s  time: %s"%(n,stop-start))

输出结果:
0  time: 20.061145067214966

Process finished with exit code 0

3.用join解决并发,竞争的问题。   需要21s  
n = 10
def work():
    time.sleep(2)      #(注意join是全局的包括这里的2s)
    global n
    temp = n
    time.sleep(0.1)
    n = temp -1
if __name__ == '__main__':
    mutex = Lock()
    l_t =[]
    start =time.time()
    for i in range(10):
        t = Thread(target=work)
        l_t.append(t)
        t.start()
        t.join()
    stop = time.time()
    print("%s  time: %s"%(n,stop-start))

输出结果:
0  time: 21.011882066726685

Process finished with exit code 0

4.用锁来解决并发,竞争的问题。
n = 10
def work():
    time.sleep(2)
    global n
    mutex.acquire()
    temp = n
    time.sleep(0.1)
    n = temp -1
    mutex.release()
if __name__ == '__main__':
    mutex = Lock()
    l_t =[]
    start =time.time()
    for i in range(10):
        t = Thread(target=work)
        l_t.append(t)
        t.start()
    for t in l_t:
        t.join()
    stop = time.time()
    print("%s  time: %s"%(n,stop-start))    #只需要3s

输出结果:
0  time: 3.0060813426971436

Process finished with exit code 0
详解join
 小结:
互斥锁是 锁 局部的. 对共享数据修改的那一部分。
join 是 锁 全局的.

5.5:多线程  and  多进程

多进程:
优点:可以利用多核
缺点:开销大
多线程:
优点:开销小
缺点:不可利用多核

from multiprocessing import Process
from threading import Thread
import time
def work():
    res = 0
    for i in range (10000000):
        res +=1
if __name__ == '__main__':
    l = []
    start = time.time()
    for i in range(4):
        p = Process(target=work)
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop = time.time()
    print('%s'%(stop-start))


输出结果:
0.9400010108947754

Process finished with exit code 0
多进程
from multiprocessing import Process
from threading import Thread
import time
def work2():
    time.sleep(2)
if __name__ == '__main__':
    l = []
    start = time.time()
    for i in range(400):
        p2 = Thread(target=work2)
        # p2 = Process(target=work2)
        l.append(p2)
        p2.start()
    for p2 in l:
        p2.join()
    stop = time.time()
    print('%s'%(stop-start))

输出结果:
2.048475742340088

Process finished with exit code 0
多线程

多进程主要用于计算功能。

多线程主要用户I/O机制中。

5.6:死锁的场景   

from threading import Thread,Lock,currentThread
mutexA =Lock()
mutexB =Lock()
class Mythread(Thread):
    def run(self):
        self.f1()
        self.f2()

    def f1(self):
        mutexA.acquire()
        print('%s 拿到 A锁'%self.name)
        mutexB.acquire()
        print('%s 拿到B 锁'%self.name)
        mutexB.release()
        mutexA.release()

    def f2(self):
        mutexB.acquire()
        print('%s 拿到 B锁' % self.name)
        time.sleep(1)   #线程2  等 B 锁, 线程1 等 A锁  这样就形成死锁了。这里sleep 1  是为了保证线程2 拿到A锁
        mutexA.acquire()
        print('%s 拿到A 锁' % 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锁

注意:这里是卡主了,没有执行完毕。
死锁场景

 5.7:递归锁:用来解决死锁的问题。(RLock)

from threading import Lock,Thread,RLock
import time

mutexB=mutexA=RLock()
class Mythread(Thread):
    def run(self):
        self.f1()
        self.f2()

    def f1(self):
        mutexA.acquire()
        print('%s 拿到 A锁'%self.name)
        mutexB.acquire()
        print('%s 拿到B 锁'%self.name)
        mutexB.release()
        mutexA.release()

    def f2(self):
        mutexB.acquire()
        print('%s 拿到 B锁' % self.name)
        time.sleep(1)   #线程2  等 B 锁, 线程1 等 A锁  这样就形成死锁了。这里sleep 1  是为了保证线程2 拿到A锁
        mutexA.acquire()
        print('%s 拿到A 锁' % 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-1 拿到A 锁
Thread-2 拿到 A锁
Thread-2 拿到B 锁
Thread-2 拿到 B锁
Thread-2 拿到A 锁
Thread-4 拿到 A锁
Thread-4 拿到B 锁
Thread-4 拿到 B锁
Thread-4 拿到A 锁
Thread-6 拿到 A锁
Thread-6 拿到B 锁
Thread-6 拿到 B锁
Thread-6 拿到A 锁
Thread-8 拿到 A锁
Thread-8 拿到B 锁
Thread-8 拿到 B锁
Thread-8 拿到A 锁
Thread-10 拿到 A锁
Thread-10 拿到B 锁
Thread-10 拿到 B锁
Thread-10 拿到A 锁
Thread-5 拿到 A锁
Thread-5 拿到B 锁
Thread-5 拿到 B锁
Thread-5 拿到A 锁
Thread-9 拿到 A锁
Thread-9 拿到B 锁
Thread-9 拿到 B锁
Thread-9 拿到A 锁
Thread-7 拿到 A锁
Thread-7 拿到B 锁
Thread-7 拿到 B锁
Thread-7 拿到A 锁
Thread-3 拿到 A锁
Thread-3 拿到B 锁
Thread-3 拿到 B锁
Thread-3 拿到A 锁

Process finished with exit code 0
递归锁

5.8:信号量: 也是一把锁     举个例子: 公厕   (Semaphore)

from threading import Thread,Semaphore,currentThread
import time,random
sm = Semaphore(5)
def task():
    sm.acquire()
    print('%s 小号'%currentThread().getName())
    time.sleep(random.randint(1,10))
    print('%s over'%currentThread().getName())
    sm.release()
if __name__ == '__main__':
    for i in range(6):
        t = Thread(target=task)
        t.start()

输出结果:
Thread-1 小号
Thread-2 小号
Thread-3 小号
Thread-4 小号
Thread-5 小号
Thread-2 over
Thread-6 小号
Thread-4 over
Thread-6 over
Thread-5 over
Thread-3 over
Thread-1 over

Process finished with exit code 0
公厕

5.9:Event:同进程一样。

event.isset() 返回event的状态值
event.wait() 如果 event.isset() == False 将 阻塞线程
event.set()  设置event的状态值为True,所有阻塞池的线程进入就绪状态,等待调度
event.clear() 回复event的状态值为false

红绿灯例子:

from threading import Event,Thread,currentThread
import time
e = Event()
def traffic_lights():
    time.sleep(2)
    e.set()
def car():
    print('% s 等待'%currentThread().getName())
    e.wait()
    print('%s  行驶'%currentThread().getName())

if __name__ == '__main__':
    for i in range(10):
        t = Thread(target=car)
        t.start()
    traffic_thread = Thread(target=traffic_lights)
    traffic_thread.start()


输出结果:
Thread-1 小号
Thread-2 小号
Thread-3 小号
Thread-4 小号
Thread-5 小号
Thread-7 等待
Thread-8 等待
Thread-9 等待
Thread-10 等待
Thread-11 等待
Thread-12 等待
Thread-13 等待
Thread-14 等待
Thread-15 等待
Thread-16 等待
Thread-5 over
Thread-6 小号
Thread-13  行驶
Thread-11  行驶
Thread-10  行驶
Thread-16  行驶
Thread-9  行驶
Thread-14  行驶
Thread-12  行驶
Thread-15  行驶
Thread-7  行驶
Thread-8  行驶
Thread-4 over
Thread-1 over
Thread-3 over
Thread-2 over
Thread-6 over

Process finished with exit code 0
红绿灯等车

检测链接例子:

from threading import Event,Thread,currentThread
import time
e = Event()
def  conn_mysql():
    count = 1
    while not e.is_set():
        if count > 3:
            raise ConnectionError('尝试链接的次数过多')
        print('第%s次尝试'%currentThread().getName())
        e.wait(1)
        count+=1
    print('%s开始链接'%currentThread().getName())

def check_mysql():
    print('%s检测mysql'%currentThread().getName())
    time.sleep(2)
    e.set()

if __name__ == '__main__':
    for i in range(2):
        t = Thread(target=conn_mysql)
        t.start()
    t = Thread(target=check_mysql)
    t.start()


输出结果:
第Thread-1次尝试
第Thread-2次尝试
Thread-3检测mysql
第Thread-1次尝试
第Thread-2次尝试
第Thread-1次尝试
第Thread-2次尝试
Thread-1开始链接
Thread-2开始链接

Process finished with exit code 0
链接

6.0:定时器:

from threading import Timer

def hello(n):
    print('hello,world',n)

t=Timer(3, hello, args=(123,))
t.start()


输出结果:等待3s
hello,world 123

Process finished with exit code 0

6.1:线程Queue:

import queue

q= queue.Queue(4)  #先进先出
q.put('first')
q.put('second')
q.put('third')
q.put('fourth')

print(q.get())
print(q.get())
print(q.get())
print(q.get())

输出结果:
first
second
third
fourth

Process finished with exit code 0
先进先出
import queue

q = queue.LifoQueue()  #先进后出
q.put('first')
q.put('second')
q.put('third')
q.put('fourth')

print(q.get())
print(q.get())
print(q.get())
print(q.get())

输出结果:
fourth
third
second
first

Process finished with exit code 0
先进后出
import queue

 q = queue.PriorityQueue()
 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')

Process finished with exit code 0
优先级队列

 

posted @ 2017-08-30 18:27  Aray007  阅读(216)  评论(0编辑  收藏  举报