+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2019-07(7)

2019-08(100)

2019-09(109)

2019-10(15)

2019-11(7)

Python多线程

发布于2020-02-10 17:29     阅读(1055)     评论(0)     点赞(7)     收藏(0)


首先我们区分一下线程和进程:

进程和线程的关系:

(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。

(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。

(3)处理机分给线程,即真正在处理机上运行的是线程

(4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

进程与线程的区别:

(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位

(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行

(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.

(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

多线程类似于同时执行多个不同程序,多线程运行有如下优点:

使用线程可以把占据长时间的程序中的任务放到后台去处理。

用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。

程序的运行速度可能加快。

在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
这里说明一下thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 “_thread”。
Python创建Thread对象语法如下:
    import threading
    threading.Thread(target=None, name=None,  args=())
  • 1
  • 2
主要参数说明:
target 是函数名字,需要调用的函数。
name 设置线程名字。
args 函数需要的参数,以元祖( tuple)的形式传入
Thread 对象主要方法说明:
run(): 用以表示线程活动的方法。
start():启动线程活动。
join(): 等待至线程中止。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
Python中实现多线程有两种方式:函数创建线程和创建线程类
函数创建线程:
import threading
import time, random, math


# idx 循环次数
def printNum(idx):
    for num in range(idx):
        # 打印当前运行的线程名字
        print("{0}\tnum={1}".format(threading.current_thread().getName(), num))
        # 随机生成一个数*2,向上取整使线程休眠,这里*2是为了时间间隔大一些
        delay = math.ceil(random.random() * 2)
        #进行休眠
        time.sleep(delay)


if __name__ == '__main__':
    #这里args()的值就是printNum函数的参数
    th1 = threading.Thread(target=printNum, args=(2,), name="thread1")
    th2 = threading.Thread(target=printNum, args=(3,), name="thread2")

    # 启动2个线程
    th1.start()
    th2.start()

    # 等待至线程中止
    th1.join()
    th2.join()
    print("{0} 线程结束".format(threading.current_thread().getName()))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
创建线程的时候,只需要传入一个执行函数和函数的参数即可完成threading.Thread实例的创建。
运行结果:
thread1 num=0
thread2 num=0
thread1 num=1
thread2 num=1
thread2 num=2
MainThread 线程结束
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
创建线程类:
import threading
import time, random, math

#继承Thread类
class MutliThread(threading.Thread):
    def __init__(self, threadName, num):
        threading.Thread.__init__(self)
        self.name = threadName
        self.num = num
    #重写run方法
    def run(self):
        for i in range(self.num):
            print("{0} i={1}".format(threading.current_thread().getName(), i))
            delay = math.ceil(random.random() * 2)
            time.sleep(delay)


if __name__ == '__main__':
    thr1 = MutliThread("thread1", 3)
    thr2 = MutliThread("thread2", 2)

    # 启动线程
    thr1.start()
    thr2.start()

    # 等待至线程中止
    thr1.join()
    thr2.join()
    print("{0} 线程结束".format(threading.current_thread().getName()))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
直接创建threading.Thread的子类来创建一个线程对象,实现多线程。通过继承Thread类,并重写Thread类的run()方法,在run()方法中定义具体要执行的任务。
运行结果:
thread1 i=0
thread2 i=0
thread1 i=1
thread2 i=1
thread1 i=2
MainThread 线程结束
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
守护线程:
上例中的join方法的作用是阻塞,等待子线程结束,join方法有一个参数是timeout,即如果主线程等待timeout,子线程还没有结束,则主线程强制结束子线程。简单来说就是使主线程等待子线程结束后再结束。如果子线程不使用join()函数,主线程和子线程是并行运行的,没有依赖关系,主线程执行了,子线程也在执行。
在多线程开发中,如果子线程设定为了守护线程,守护线程会等待主线程运行完毕后被销毁。一个主线程可以设置多个守护线程,守护线程运行的前提是,主线程必须存在,如果主线程不存在了,守护线程会被销毁。
import threading, time

def run(taskName):
    print("任务:", taskName)
    time.sleep(2)
    print("{0} 任务执行完毕".format(taskName))  # 查看每个子线程

if __name__ == '__main__':
    start_time = time.time()
    for i in range(3):
        thr = threading.Thread(target=run, args=("task-{0}".format(i),))
        thr.start()

    # 查看主线程和当前活动的所有线程数,active_count方法是查看当前线程数
    print("{0}线程结束,当线程数量={1}".format(threading.current_thread().getName(), threading.active_count()))
    print("消耗时间:", time.time() - start_time)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
运行结果:
    任务: task-0
    任务: task-1
    任务: task-2
    MainThread线程结束,当线程数量=4
    消耗时间: 0.0009751319885253906
    task-2 任务执行完毕
    task-0 任务执行完毕
    task-1 任务执行完毕
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
从返回结果可以看出,当前的线程个数是4,线程个数=主线程数 + 子线程数,在本例中有1个主线程和3个子线程。主线程执行完毕后,等待子线程执行完毕,程序才会退出。
在这里我们把所有的子线程都设置为守护线程。子线程变成守护线程后,只要主线程执行完毕,程序不管子线程有没有执行完毕,程序都会退出。使用线程对象的setDaemon(True)函数来设置守护线程。
import threading, time

def run(taskName):
    print("任务:", taskName)
    time.sleep(2)
    print("{0} 任务执行完毕".format(taskName))

if __name__ == '__main__':
    start_time = time.time()
    for i in range(3):
        thr = threading.Thread(target=run, args=("task-{0}".format(i),))

        # 把子线程设置为守护线程,一定在启动线程前设置
        thr.setDaemon(True)
        thr.start()

    # 查看主线程和当前活动的所有线程数
    thrName = threading.current_thread().getName()
    thrCount = threading.active_count()
    print("{0}线程结束,当线程数量={1}".format(thrName, thrCount))
    print("消耗时间:", time.time() - start_time)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
运行结果:
    任务: task-0
    任务: task-1
    任务: task-2
    MainThread线程结束,当线程数量=4
    消耗时间: 0.0010023117065429688
  • 1
  • 2
  • 3
  • 4
  • 5
这里就可以看出,设置了守护线程以后当主线程结束了,子线程也随之销毁。

线程互斥锁同步线程

import threading

balance = 100

def change(num, counter):
    global balance
    for i in range(counter):
        balance += num
        balance -= num
        if balance != 100:
            # 如果输出这句话,说明线程不安全
            print("balance=%d" % balance)
            break

if __name__ == "__main__":
    thr1 = threading.Thread(target=change, args=(100, 500000), name='t1')
    thr2 = threading.Thread(target=change, args=(100, 500000), name='t2')
    thr1.start()
    thr2.start()
    thr1.join()
    thr2.join()
    print("{0} 线程结束".format(threading.current_thread().getName()))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
运行结果:
    balance=200
    MainThread 线程结束
  • 1
  • 2
本例中在for循环的过程中,由于线程同时进行,我们定义的全局变量balance也同时被调用,只要循环次数足够,一定会有一次在t1执行balance += num后t2也执行balance += num,此时balance值为300,然后t1在进行balance -= num,此时balance值为200,进入if循环。
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire([timeout])
#释放
mutex.release()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
import threading

balance = 100

lock = threading.Lock()

def change(num, counter):
    global balance
    for i in range(counter):
        # 先要获取锁
        lock.acquire()

        balance += num
        balance -= num

        # 释放锁
        lock.release()

        if balance != 100:
            # 如果输出这句话,说明线程不安全
            print("balance=%d" % balance)
            break

if __name__ == "__main__":
    thr1 = threading.Thread(target=change,args=(100,500000),name='t1')
    thr2 = threading.Thread(target=change,args=(100,500000),name='t2')
    thr1.start()
    thr2.start()
    thr1.join()
    thr2.join()
    print("{0} 线程结束".format(threading.current_thread().getName()))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
运行结果:
MainThread 线程结束
  • 1
发布了7 篇原创文章 · 获赞 5 · 访问量 239


所属网站分类: 技术文章 > 博客

作者:what

链接: https://www.pythonheidong.com/blog/article/231020/

来源: python黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

7 0
收藏该文
已收藏

评论内容:(最多支持255个字符)