关于服务器:你要的线程

6次阅读

共计 4991 个字符,预计需要花费 13 分钟才能阅读完成。

线程概述

多线程相似于同时执行多个不同程序,多线程运行有如下长处:

应用线程能够把占据长时间的程序中的工作放到后盾去解决。

用户界面能够更加吸引人,这样比方用户点击了一个按钮去触发某些事件的解决,能够弹出一个进度条来显示解决的进度

程序的运行速度可能放慢

在一些期待的工作实现上如用户输出、文件读写和网络收发数据等,线程就比拟有用了。在这种状况下咱们能够开释一些宝贵的资源如内存占用等等。

线程在执行过程中与过程还是有区别的。每个独立的过程有一个程序运行的入口、程序执行序列和程序的进口。然而线程不可能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行管制。

每个线程都有他本人的一组 CPU 寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的 CPU 寄存器的状态。

指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在过程失去上下文中运行的,这些地址都用于标记领有线程的过程地址空间中的内存。

线程能够被抢占(中断)。

在其余线程正在运行时,线程能够临时搁置(也称为睡眠)— 这就是线程的让步。

线程就是在程序运行过程中,执行程序代码的一个分支,每个运行的程序至多都有一个线程


创立多线程:thread

调用 thread 模块中的 start_new_thread() 函数来产生新线程

thread.start_new_thread (function, args[, kwargs] )

function - 线程函数。args - 传递给线程函数的参数, 他必须是个 tuple 类型。kwargs - 可选参数。python 多线程的开启

start()

import threading

import time

def task():

    time.sleep(1) 

     print("以后线程:", threading.current_thread().name)

if__name__ =='__main__':

    for_inrange(5):

         sub_thread = threading.Thread(target=task) 

         sub_thread.start()

线程之间执行是无序的

主线程会期待所有的子线程完结后才完结

主线程会期待所有的子线程完结后才完结,如果须要能够设置守护主线程

多线程的特点(共享全局变量)

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

除了应用办法外,线程模块同样提供了 Thread 类来解决线程,Thread 类提供了以下办法:

run(): 用以示意线程流动的办法。start(): 启动线程流动。join([time]): 期待至线程停止。这阻塞调用线程直至线程的 join() 办法被调用停止 - 失常退出或者抛出未解决的异样 - 或者是可选的超时产生。isAlive(): 返回线程是否流动的。getName(): 返回线程名。setName(): 设置线程名。

自定义线程

自定义线程不能指定 target,因为自定义线程外面的工作都对立在 run 办法外面执行

启动线程对立调用 start 办法,不要间接调用 run 办法, 因为这样不是应用子线程去执行工作


import threading

# 自定义线程类

classMyThread(threading.Thread):

# 通过构造方法取接管工作的参数

    def__init__(self, info1, info2):

    # 调用父类的构造方法

        super(MyThread, self).__init__() 

         self.info1 = info1 

         self.info2 = info2

    # 定义自定义线程相干的工作

    def test1(self):

        print(self.info1)

    deftest2(self):

        print(self.info2)

    # 通过 run 办法执行相干工作

    def run(self):

        self.test1() 

         self.test2()

创立自定义线程


my_thread = MyThread("测试 1","测试 2")

启动


my_thread.start()

资源竞争问题

多线程能够共享全局变量

多线程共享全局变量,很不便在多个线程间共享数据

因为多线程同时对全局变量进行操作,很容易呈现资源竞争问题


import threading

# 定义全局变量

g_num =0

# 循环一次给全局变量加 1

defsum_num1():

    for i in range(1000000):

        globalg_num 

         g_num +=1

    print("sum1:", g_num)

# 循环一次给全局变量加 1

defsum_num2():

    for i in range(1000000):

        global g_num 

         g_num +=1

    print("sum2:", g_num)

if__name__ =='__main__':

# 创立两个线程

first_thread = threading.Thread(target=sum_num1) 

 second_thread = threading.Thread(target=sum_num2)

# 启动线程

first_thread.start()

# 启动线程

second_thread.start()

咱们能够看到多线程同时对全局变量操作数据产生了谬误

多线程同时操作全局变量导致数据可能呈现谬误的起因剖析

两个线程 first_thread 和 second_thread 都要对全局变量 g_num(默认是 0) 进行加 1 运算,然而因为是多线程同时操作,有可能呈现上面状况:

在 g_num= 0 时,first_thread 获得 g_num=0。此时零碎把 first_thread 调度为”sleeping”状态,把 second_thread 转换为”running”状态,t2 也取得 g_num=0

而后 second_thread 对失去的值进行加 1 并赋给 g_num,使得 g_num=1

而后零碎又把 second_thread 调度为”sleeping”,把 first_thread 转为”running”。线程 t1 又把它之前失去的 0 加 1 后赋值给 g_num。

这样导致尽管 first_thread 和 first_thread 都对 g_num 加 1,但后果依然是 g_num=1

多线程资源竞争解决办法

线程同步

线程同步: 保障同一时刻只能有一个线程去操作全局变量 同步: 就是协同步调,按预约的先后秩序进行运行。如: 你说完,我再说, 好比现实生活中的对讲机

多个线程同时对同一个全局变量进行操作,会有可能呈现资源竞争数据谬误的问题

线程同步形式能够解决资源竞争数据谬误问题,然而这样有多任务变成了单任务。

互斥锁

threading 模块中定义了 Lock 变量,这个变量实质上是一个函数,能够不便的解决锁定:

创立锁

mutex = threading.Lock()

锁定

mutex.acquire()

开释

mutex.release()

import threading

# 定义全局变量

g_num =0

# 创立全局互斥锁

lock = threading.Lock()

# 循环一次给全局变量加 1

def sum_num1():

# 上锁


    lock.acquire()

    for i in range(1000000):

        global g_num

        g_num +=1

    print("sum1:", g_num)


# 开释锁

    lock.release()

# 循环一次给全局变量加 1

def sum_num2():

# 上锁

    lock.acquire()

    for iin range(1000000):

        global g_num

        g_num +=1

    print("sum2:", g_num)

# 开释锁

    lock.release()

if __name__ =='__main__':

# 创立两个线程

    first_thread = threading.Thread(target=sum_num1)

second_thread = threading.Thread(target=sum_num2)

# 启动线程

    first_thread.start()

second_thread.start()

提醒:加上互斥锁,哪个线程抢到这个锁咱们决定不了,哪个线程抢到锁哪个线程先执行,没有抢到的线程须要期待

    # 加上互斥锁多任务霎时变成单任务,性能会降落,也就是说同一时刻只能有一个线程去执行

当一个线程调用锁的 acquire() 办法取得锁时,锁就进入“locked”状态。

每次只有一个线程能够取得锁。如果此时另一个线程试图取得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到领有锁的线程调用锁的 release() 办法开释锁之后,锁进入“unlocked”状态。

线程调度程序从处于同步阻塞状态的线程中抉择一个来取得锁,并使得该线程进入运行(running)状态。

确保了某段要害代码只能由一个线程从头到尾残缺地执行

锁的害处:

多线程执行变成了蕴含锁的某段代码实际上只能以单线程模式执行,效率就大大地降落了

锁应用不好就容易呈现死锁状况

死锁


import threading

import time

# 创立互斥锁

lock = threading.Lock()

# 依据下标去取值,保障同一时刻只能有一个线程去取值

def get_value(index):

# 上锁

    lock.acquire()

    print(threading.current_thread())

    my_list = [3,6,8,1]

# 判断下标开释越界

    if index >=len(my_list):

        print("下标越界:", index)

        return

    value = my_list[index]

    print(value)

    time.sleep(0.2)

# 开释锁

    lock.release()

if __name__ =='__main__':

# 模仿大量线程去执行取值操作

    for i in range(30):

        sub_thread = threading.Thread(target=get_value,args=(i,))

        sub_thread.start()

应用互斥锁的时候须要留神死锁的问题,要在适合的中央留神开释锁

死锁一旦产生就会造成利用的进行响应

线程优先级队列(Queue)

Python 的 Queue 模块中提供了同步的、线程平安的队列类,包含 FIFO(先入先出 ) 队列 Queue,LIFO(后入先出)队列 LifoQueue,和优先级队列 PriorityQueue。这些队列都实现了锁原语,可能在多线程中间接应用。能够应用队列来实现线程间的同步。

Queue 模块中的罕用办法:

Queue.qsize() 返回队列的大小

Queue.empty() 如果队列为空,返回 True, 反之 False

Queue.full() 如果队列满了,返回 True, 反之 False

Queue.full 与 maxsize 大小对应

Queue.get([block[, timeout]]) 获取队列,timeout 等待时间

Queue.get_nowait() 相当 Queue.get(False)

Queue.put(item) 写入队列,timeout 等待时间

Queue.put_nowait(item) 相当 Queue.put(item, False)

Queue.task_done() 在实现一项工作之后,Queue.task_done() 函数向工作曾经实现的队列发送一个信号

Queue.join() 实际上意味着等到队列为空,再执行别的操作 

线程,你学废了吗?

正文完
 0