一 线程的理论

 什么是线程:线程就是开启一个进程,而执行进程里任务的就是线程。在一个进程里面可以创建多个线程,也叫做多线程。

 每创建一个主进程,同时就会产生一个控制线程,来执行主进程里面的任务。而这个线程就叫做主线程。主线程是可以用了开辟其他的子线程的,分发多个子线程。子线程下面还可以再有子线程

 在一个进程中创建的多个线程都可以共享这个进程里面的资源。

 为什么线程的开销小:创建线程是不会产生单独的内存空间的,而是在他的进程里面直接就会产生。

  线程和进程之间的关系

      1 线程一定是寄托在进程里面的。

      2一个进程可以分发多个线程,线程是最小的执行单位

      3 进程和线程的切换,切换时操作系统操作的,

        4切换的操作者:

                         4,1 时间片:就是切换的时候停止的时间

                         4,2 I/O操作切换:I/O卡住时,是不会占用CPU的。

        4,3 优先级切换。

二 线程和进程的区别

 区别1:在同一个进程下的多个线程是可以共享这个进程里面的资源的。

    进程之间都会有独立的内存空间的,他们之间没有人任何关系的。

 区别2:线程之间是可以直接访问他们共同进程下的数据并进行修改。

    而进程之间的访问是需要依赖于ipc的。

 区别3:线程的创建是不需要重新开启单独的内存空间的。

    每一个进程的产生,就会产生一个内存空间的。

 区别4:同一个进程下的多个线程之间是可以相互影响的

    进程之间是没有任何影响的。

 区别5:同一个进程下的多个线程修改数据,都会影响到其他线程的

    进程之间,修改数据对其他的进程是没有任何影响的

三 为什么要用多线程

 多个任务在一个进程里面完成,那就必须在这个进程里面开启多个线程。

  1 多线程共享着一个进程的内存空间

  2 线程比进程更轻量级,而线程比进程更加容易的创建和撤销。创建一个线程的速度会比创建一个进程的速度快上10到100倍之间。

  3 如果多个线程都是CPU密集型时,并不能获得性能上的提升。拥有多线程允许任务之间互相重叠运行,从而加快程序的执行速度

  4 为了利用多核,开启多个线程比开启多个进程的开销要小的多。在同一时候,一个CPU只能执行一个线程,所以CPU有几核,就能同时执行几个线程(而在cpython中,这个并不能实用,因为在cpython中,一个进程里面只能让一个线程运行。)

 线程才是被CPU真正执行的,而在进程之间的线程是没有任何关系的。

线程的详细介绍:http://www.cnblogs.com/linhaifeng/articles/7430082.html

四 threading模块:开启子线程的模块

 Thread:开启子线程

 target:后面加上一个函数名

 args:传参数,以元组的格式传入

 格式:线程=threading.Thread(target=函数名,args=(参数,))

 strart:向操作系统发送开启线程的信息(启动线程)

线程的创建方式:

方式1:

# import threading
# import os
# def work(n):
#     print('%s is working %s'%(os.getpid(),n))
#
#
# if __name__=='__main__':
#     for i in range(10):
#         t=threading.Thread(target=work,args=(i,))
#         t.start()
#     print('主线程》》》',os.getpid())

 方式2:

import threading
class Mythread(threading.Thread):
    def __init__(self,n):
        super().__init__()
        self.n=n
    def run(self):
        print('%s is working' % self.n)
if __name__=='__main__':
    for i in range(10):
        t=Mythread(i)
        t.start()
        print('主线程》》》')

 在同一个进程下,子线程和主线程使用的是统一个pid

 join:等待线程执行结束

import threading
import os
import time
import random
def work(n):
    time.sleep(random.random())
    print('%s is working %s'%(os.getpid(),n))

if __name__=='__main__':
    for i in range(10):
        t=threading.Thread(target=work,args=(i,))
        t.start()
        t.join()
    print('主线程》》》',os.getpid())

 threading的其他使用方法:

  getName:获取线程名

  setName:设置线程名

  carrent_thread:获取当前的线程对象

  enumerate:查看当前活跃的线程对象,以列表的格式返回

  activeCount:查看当前活跃的线程数目

import threading
import os
import time
import random
def work(n):
    time.sleep(random.random())
    print('%s is working %s'%(os.getpid(),n))  
    print(threading.current_thread().getName())     #获取当前线程名

if __name__=='__main__':
    for i in range(10):
        t=threading.Thread(target=work,args=(i,))
        t.start()
        print(threading.enumerate())    #查看当前活跃的对象
        print(threading.activeCount())    #查看活跃的数目
    print('主线程》》》',os.getpid())

 线程执行完毕,被回收的时间长短是不一样的。

守护线程:守护线程会在主线程执行结束后,自动的结束。

主线程从执行的意义上来讲就是一个主进程,主线程的结束会在除守护线程执行结束后就会结束。

daemon=True:创建守护线程

import threading
import os
import time
import random
def work(n):
    print('%s is working %s'%(os.getpid(),n))
    time.sleep(random.random())
    print('%s is ending %s'%(os.getpid(),n))
def foo(n):
    print('%s is fooing %s'%(os.getpid(),n))
    time.sleep(random.random())
    print('%s is ending %s'%(os.getpid(),n))

if __name__=='__main__':
    t1=threading.Thread(target=work,args=(100,))
    t2=threading.Thread(target=foo,args=(200,))
    t1.daemon=True
    t1.start()
    t2.start()
    print('主线程》》》',os.getpid())

守护线程等待非守护线程的结束是为了关闭进程。

五 GIL(互斥锁)

 1 全局解释互斥锁:保证了数据的安全性。而GIL是cpython解释器的一种特性。

 2 互斥锁(mutex):限制了cpython中多个线程的执行数量。与cpython的内存管理有关。

 3 cpython中有一个回收机制,绑定的关系引用技术为0时,就会自动的回收调。

 一把互斥锁只能保证一类数据的安全性。

import threading
import time
import random
n=20
def work():
    global n
    loak.acquire()
    time.sleep(random.random())
    n-=1
    loak.release()
if __name__=='__main__':
    li=[]
    loak=threading.Lock()
    for i in range(20):
        t=threading.Thread(target=work)
        li.append(t)
        t.start()
    for t in li:
        t.join()
    print(n)

 假如不加上锁,结果会不一样,如下:

import threading
import time
import random
n=200
def work():
    global n
    time.sleep(random.random())
    n-=1
if __name__=='__main__':
    for i in range(200):
        t=threading.Thread(target=work)
        t.start()
    t.join()
    print(n)

 GIL锁只能保证cpython解释器中的代码安全,不能保证自己的代码安全性,如果想要保证自己代码的安全性,就需要给自己的代码加上一把互斥锁

  有了GIL锁,就能保证cpython中的进程中的多个线程一只能执行一个。

对于计算密集型来说,在多核CPU下开启多个进程的效率比较高

#使用线程进行计算密集型
# import threading
# import time
# def work():
#     a=1
#     for i in range(1000000):
#         a*=i
# if __name__=='__main__':
#     li=[]
#     start = time.time()
#     for i in range(100):
#         t=threading.Thread(target=work)     #13.447768926620483
#         li.append(t)
#         t.start()
#     for t in li:
#         t.join()
#     print(time.time() - start)


# 使用进程实现计算密集型
# import multiprocessing
# import time
# def work():
#     a=1
#     for i in range(1000000):
#         a*=i
# if __name__=='__main__':
#     li=[]
#     start = time.time()
#     for i in range(100):
#         t=multiprocessing.Process(target=work)    #8.421481609344482
#         li.append(t)
#         t.start()
#     for t in li:
#         t.join()
#     print(time.time() - start)

 

对于IO密集型来说,多核CPU和单核CPU的效果是一样的,开启多线程的效率会比开启多进程的效率要高,并且还能保证数据的安全性。

#使用线程进行IO密集型
# import threading
# import time
# def work():
#     time.sleep(2)
# if __name__=='__main__':
#     li=[]
#     start = time.time()
#     for i in range(10):
#         t=threading.Thread(target=work)     #2.0028274059295654
#         li.append(t)
#         t.start()
#     for t in li:
#         t.join()
#     print(time.time() - start)


# 使用进程实现IO密集型
# import multiprocessing
# import time
# def work():
#     time.sleep(2)
# if __name__=='__main__':
#     li=[]
#     start = time.time()
#     for i in range(10):
#         t=multiprocessing.Process(target=work)    #2.953113079071045
#         li.append(t)
#         t.start()
#     for t in li:
#         t.join()
#     print(time.time() - start)

 

posted on 2017-10-13 20:12  方少0410  阅读(190)  评论(0编辑  收藏  举报