并发编程 线程

一、什么是线程?

线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

image

线程发展:
60年代,在OS中能拥有资源和独立运行的基本单位是进程,然而随着计算机技术的发展,进程出现了很多弊端,一是由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入轻型进程;二是由于对称多处理机(SMP)出现,可以满足多个运行单位,而多个进程并行开销过大。

因此在80年代,出现了能独立运行的基本单位——线程(Threads)。

二、进程与线程的关系

  1. 地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。

  2. 通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。

  3. 调度和切换:线程上下文切换比进程上下文切换要快得多。

  4. 在多线程操作系统中,进程不是一个可执行的实体。

三、线程的特点

  1. 多线程共享一个进程的地址空间

  2. 线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用

  3. 若多个线程都是cpu密集型的,那么并不能获得性能上的增强,但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠运行,从而会加快程序执行的速度。

  4. 在多cpu系统中,为了最大限度的利用多核,可以开启多个线程,比开进程开销要小的多。(这一条并不适用于python)

四、python模块threading

在python中使用模块threading操作线程,threading模块和multiprocess用法基本一致。

image

创建线程的两种方式

方式一:

from threading import Thread
import time

def test(name):
    time.sleep(2)
    print('%s say hello' %name)

if __name__ == '__main__':
    t=Thread(target=test,args=('xie',))
    t.start()
    print('主线程')

方式二:

from threading import Thread
import time

class MyThread(Thread):
    def __init__(self,name):
        super().__init__()
        self.name=name

    def run(self):
        time.sleep(2)
        print('%s say hello' % self.name)


if __name__ == '__main__':
    t = MyThread('xie')
    t.start()
    print('主线程')

同进程中线程之间数据共享

from  threading import Thread

def func():
	global n
	n=0

if __name__ == 'main':
	n=1
	t=Thread(target=func)
	t.start()
	t.join()
	print('主',n)  # 主 0

线程模块中的其他方法

# t.isAlive(): 返回线程是否活动的
# t.getName(): 返回线程名
# t.setName(): 设置线程名

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

守护线程

随主线程运行完毕而停止并被回收。对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕。

from threading import Thread
import time

def test(name):
    time.sleep(2)
    print('%s say hello' %name)

if __name__ == '__main__':
    t=Thread(target=test,args=('xie',))
    t.daemon = True  # 必须在t.start()之前设置
    t.start()

    print('主线程')
    print(t.is_alive())
    '''
    主线程
    True
    '''
from threading import Thread
import time

def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")


t1=Thread(target=foo)
t2=Thread(target=bar)

t1.daemon=True
t1.start()
t2.start()
print("main-------")
# 输出依次:
# 123
# 456
# main-------
# end123
# end456

互斥锁

模拟100个人同时抢票的情况,在不加锁的情况下数据是不安全的,就很有可能造成最后的数据与逻辑不符。

from threading import Thread
import time

def grab():
    global n
    temp=n
    time.sleep(0.1)
    n=temp-1
if __name__ == '__main__':
    n=100
    l=[]
    for i in range(100):
        t=Thread(target=grab)
        l.append(p)
        t.start()
    for t in l:
        t.join()

    print(n) #结果可能为99

我们需要加锁保证数据的安全,即把并行改成串行,保证同一时间内只有一个线程执行对数据的操作。threading与multiprocess类似,也有Lock。

from threading import Thread,Lock
import time

def grab():
    global n
    lock.acquire()
    temp=n
    time.sleep(0.1)
    n=temp-1
    lock.release()
if __name__ == '__main__':
    lock=Lock()
    n=100
    l=[]
    for i in range(100):
        t=Thread(target=grab)
        l.append(t)
        t.start()
    for t in l:
        t.join()

    print(n)  # 结果肯定为0,由原来的并发执行变成串行,牺牲了执行效率保证了数据安全

posted @ 2022-01-14 17:31  它叫鸮  阅读(44)  评论(0编辑  收藏  举报