Python学习---线程基础学习

线程基础

什么是线程(thread)

线程是CPU调度能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流[换言之,线程就是一堆指令集合],一个进程中可以并发多个线程,每条线程并行执行不同的任务

线程的执行特性

线程只有 3 个基本状态:就绪,执行,阻塞。

线程存在 5 种基本操作来切换线程的状态:派生,阻塞,激活,调度,结束。

什么是进程(Process)
进程,是并发执行的程序在执行过程中操作系统分配和管理资源的基本单位,是一个动态概念,竟争计算机系统资源的基本单位。每一个进程都有一个自己的地址空间,即进程空间或(虚空间)。进程空间的大小 只与处理机的位数有关

进程开辟子进程,子进程完全Copy父进程,比如父进程占用20M,子进程也占用20M,所以开进程比开线程更消耗资源

线程与进程的区别

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

线程可以直接访问其进程的数据段; 进程拥有其父进程的数据段的自己的副本。

线程可以直接与其进程的其他线程通信; 进程必须使用进程间通信与兄弟进程进行通信。

新线程很容易创建; 新线程需要重复父线程。比如父进程占用20M,子进程也占用20M,所以开进程比开线程更消耗资源

线程之间可以相互操作,进程之间不可以

对主线程的更改(取消,优先级更改等)可能会影响进程的其他子线程线程; 父进程的更改不会影响子进程。

问:线程执行快还是执行进程快?[陷阱题]

答: 一样快,跑的内容是一样的

Python可以创建多进程,不严格来说因为有GIL,Python没有多线程,但是可以利用多进程来解决[多进程下不能实现数据共享,可以通过其他解决,协程,堆等方案]实现CPU多核的利用

如果在py里面,任务是IO密集型[不是一直调用CPU执行任务,会有sleep等IO阻塞],多线程,如果计算密集型,可以考虑C开发

线程创建

线程的创建:

1. 直接调用,threading.Thread(target=sayhi,args=(1,)

2. 继承式调用:

直接调用:

import time
import threading
begin=time.time()
def bar(n):
    print('bar%s'%n)
    time.sleep(3)
def foo(n):
    print('foo%s' %n)
    time.sleep(2)

t1 = threading.Thread(target=bar, args=(1,)) # 创建t1线程对象
t2 = threading.Thread(target=foo, args=(2,)) # 创建t2线程对象
t1.start()   # 线程启动,开始抢占CPU资源
t2.start()   # 线程启动,开始抢占CPU资源
end=time.time()
t1.join()   # 线程阻塞,执行完天t1后执行主线程
t2.join()   # 线程阻塞,执行完天t2后执行主线程
end2 = time.time()
print('此时有3个线程,主线程,t1线程, t2线程')
print(end-begin)
print(end2-begin) 

image

继承式调用:

import threading
import time
class MyThread(threading.Thread):
    def __init__(self, num):
        threading.Thread.__init__(self)
        self.num = num
    def run(self):  # 定义每个线程要运行的函数
        print("running on number:%s" % self.num)
        time.sleep(3)
if __name__ == '__main__':
    t1 = MyThread(1)
    t2 = MyThread(2)
    t1.start()
    t2.start()

线程常用方法

Thread.join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

import threading
from time import ctime,sleep
import time

def music(func):
    for i in range(2):
        print ("Begin listening to %s. %s" %(func,ctime()))
        sleep(4)
        print("end listening %s"%ctime())

def move(func):
    for i in range(2):
        print ("Begin watching at the %s! %s" %(func,ctime()))
        sleep(5)
        print('end watching %s'%ctime())

threads = []
t1 = threading.Thread(target=music,args=('七里香',))
threads.append(t1)
t2 = threading.Thread(target=move,args=('阿甘正传',))
threads.append(t2)

if __name__ == '__main__':

    for t in threads:
        t.start()            # t1.start(). t2.start()
        # t.join()           # 串行执行,t1.start()后,进入t1.join()等到t1执行完后在执行t2
    # t.join()               # Python中默认取最后一个for循环的t2,等价于t2.join()
    # t1.join()              # 主线程的print()会在第8秒出,最后打印end movies
    t.join()                 # t2.join(),t2执行end后程序结束
    print ("all over %s" %ctime()) 

setDemaon(True):

将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。这个方法基本和join是相反的。当我们在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon(True)

import threading
from time import ctime,sleep
import time

def music(func):
    for i in range(2):
        print ("Begin listening to %s. %s" %(func,ctime()))
        sleep(4)
        print("end listening %s"%ctime())

def move(func):
    for i in range(2):
        print ("Begin watching at the %s! %s" %(func,ctime()))
        sleep(5)
        print('end watching %s'%ctime())

threads = []
t1 = threading.Thread(target=music,args=('七里香',))
threads.append(t1)
t2 = threading.Thread(target=move,args=('阿甘正传',))
threads.append(t2)

if __name__ == '__main__':
    # t2.setDaemon(True)    # 执行t1后就结束程序,也就是说不执行 print('end watching %s'%ctime()) 
    for t in threads:
        t.setDaemon(True)    # 将线程声明为守护线程,必须在start() 方法调用之前设置
        t.start()            # t1.start(). t2.start()
    print ("all over %s" %ctime())

image

Thread提供线程的方法

thread 模块提供的其他方法:
 threading.currentThread(): 返回当前的线程变量。
 threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
 threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
 除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
 run(): 用以表示线程活动的方法。
 start():启动线程活动。
 join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
 isAlive(): 返回线程是否活动的。
 getName(): 返回线程名。
 setName(): 设置线程名。

python 的GIL(Global Interpreter Lock)

解释器原因: 由于Cpython解释的原因,同一时刻只能解释器只能调用一个线程,可以理解Python没有多线程

CPython实现细节:在CPython中,由于全局解释器锁定,只有一个线程可以一次执行Python代码(即使某些面向性能的库可能会克服此限制)。 如果您希望您的应用程序更好地利用多核计算机的计算资源,建议您使用多处理。 但是,如果要同时运行多个I / O密集型任务的话,线程仍然是一个合适的模型。

 

【更多参考】http://www.cnblogs.com/yuanchenqi/articles/5733873.html

posted @ 2018-07-28 22:46  小a玖拾柒  阅读(272)  评论(0编辑  收藏  举报