python 多线程

什么是线程:

在一个进程中,默认就有一个线程。将进程比喻成工厂车间的话,线程就相当于车间里的一条流水线。进程负责将资源整合到一起,而线程才是cpu上面的执行单位。

 

多线程:

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

 

进程和线程的区别:

 

线程共享创建它的进程的地址空间。而进程有自己单独的地址空间。

线程共享其进程资源,而进程完全copy其父进程资源。

进程中的线程与线程可以直接通信,进程必须依赖进程间通信与同级进程通信。

创建线程的开销远远小于进程,不用重复开辟内存空间。

 

开启线程的两种方式:

#方式一
from threading import Thread
def talk():
    print("%s is running"%os.getpid())
if __name__ == '__main__':
    t = Thread(target=talk)
    t.start()
    print('')

#方式二
from threading import Thread
import os
class MyThread(Thread):
    def __init__(self,name):
        super().__init__()
        self.name = name
    def run(self):
        print("pid %s name [%s] is running"%(os.getpid(),self.name))
if __name__ == '__main__':
    t = MyThread("egon")
    t.start()
    print("",os.getpid())
创建线程的两种方式

同一进程下开启多线程和多进程的区别:

from multiprocessing import Process
from threading import Thread
def talk():
    start = time.time()
    print("%s is running"%os.getpid())
    print(time.time()-start)
if __name__ == '__main__':
    #多进程,每个进程都有不同的ip
    t1 = Process(target=talk)
    t2 = Process(target=talk)
    t3 = Process(target=talk)
    t1.start()
    t2.start()
    t3.start()
    #多线程,每个线程的ip都和主线程一样
    t4 = Thread(target=talk)
    t5 = Thread(target=talk)
    t6 = Thread(target=talk)
    t4.start()
    t5.start()
    t6.start()
    print('',os.getpid())
View Code

练习:

#服务端
import threading
from socket import *
phone = socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
phone.bind(("192.168.19.115",8888))
phone.listen(5)
def talk(conn,addr):
    while True:
        try:
            data = conn.recv(1024)
            if not data:break
            conn.send(data.upper())
        except Exception:
            break
    conn.close()
if __name__ == '__main__':
    while True:
        conn,addr = phone.accept()
        p = threading.Thread(target=talk,args=(conn,addr))
        p.start()
#客户端
from socket import *
c = socket(AF_INET,SOCK_STREAM)
c.connect(('192.168.19.115',8888))

while True:
    msg = input(">>>:".strip())
    if not msg:continue
    c.send(msg.encode("utf-8"))
    data = c.recv(1024)
    print(data.decode('utf-8'))
c.close()
多线程实现并发
from threading import Thread
input_lis = []
format_lis = []
def talk():
    while True:
        msg = input(">>>:").strip()
        if not msg:continue
        input_lis.append(msg)
def format():
    while True:
        if input_lis:
            res = input_lis.pop()
            format_lis.append(res.upper())
def save():
    with open("db.txt",'a')as f:
        while True:
            if format_lis:
                f.write("%s\n"%(format_lis.pop()))
                f.flush()
if __name__ == '__main__':
    t1 = Thread(target=talk)
    t2 = Thread(target=format)
    t3 = Thread(target=save)

    t1.start()
    t2.start()
    t3.start()
模拟文件存储的过程

线程的其它属性和方法:

from threading import Thread,currentThread,activeCount
import os,time,threading
def talk():
    print("%s is running "%(currentThread().getName()))
if __name__ == '__main__':
    t = Thread(target=talk)
    t = Thread(target=talk,name = "egon")

    t.start()
    print(t.name)#线程名
    print(t.getName())
    print(t.is_alive())
    print(currentThread().getName())#默认线程名
    print(threading.enumerate())#返回一个列表,同activeCount
    time.sleep(3)
    print(t.is_alive())#是否存活
    print("",activeCount())#个数
其它属性和方法

守护线程:


无论是守护进程还是守护线程,都要遵循:守护进程(线程)会随着主进程(线程)运行结束而销毁。

对进程来说:运行结束就是主进程代码运行结束

对线程来说:运行结束必须等到所有的非守护线程运行结束后,主线程才算运行结束,因为主线程的结束以为这该进程的结束,进程的整体资源都将被回收。

from threading import Thread,currentThread
import time
def talk1():
    time.sleep(10)#这里如果是小于或等于非守护进程的睡眠时间,将会执行下面代码
    print("%s is running"%currentThread().getName())#10秒时间足以让主线程和非守护线程执行结束,这句不会打印,随着主线程结束而结束
def talk2():
    time.sleep(2)
    print("%s is running"%currentThread().getName())
if __name__ == '__main__':
    t1 = Thread(target=talk1)
    t2 = Thread(target=talk2)
    t1.daemon = True
    t1.start()
    t2.start()
    print('主程序执行完成')#守护线程随着主线程的结束而结束,注意主线程是当所有的非守护线程结束后才会结束

全局解释锁GIL:

GIL的本质还是一把互斥锁,所有的互斥锁的本质都是一样,那就是将并发转成串行,以此来控制同一时间内共享数据只能被一个任务修改,从而保护数据的安全性。

保护不同的数据安全,就应该加不同的锁

GIL是把同一进程内,多个线程争抢一把锁,保证同一时刻只有一个线程在运行

在线程执行过程中与I/O,运行时间过长的时候会强制释放GUL供其他线程使用。知道该线程再次抢到GUL,基于上次继续运行。

 

from threading import Thread,Lock
import time
n = 100
def work():
    global n
    tem = n
    time.sleep(0.5)
    n = tem-1
if __name__ == '__main__':
    mutex = Lock()
    t_lis = []
    s = time.time()
    for i in range(100):
        t = Thread(target=work)
        t_lis.append(t)
        t.start()
    for t in t_lis:
        t.join()
    print("%s:%s"%(time.time()-s,n))
    # 0.5403599739074707:99执行了100次但是结果是99,出现数据错乱


from threading import Thread,Lock
import time
n = 100
def work():
    mutex.acquire()
    global n
    tem = n
    time.sleep(0.5)
    n = tem-1
    mutex.release()
if __name__ == '__main__':
    mutex = Lock()
    t_lis = []
    s = time.time()
    for i in range(100):
        t = Thread(target=work)
        t_lis.append(t)
        t.start()
    for t in t_lis:
        t.join()
    print("%s:%s"%(time.time()-s,n))
    #   50.05104112625122:0加上应用层互斥锁,运行时间长,数据安全
View Code

 

posted on 2017-08-29 20:32  keepDoing  阅读(166)  评论(0编辑  收藏  举报

导航