并发编程之多线程

1.什么是线程?

线程指的是程序的执行线路,相当于一条流水线,包含了程序的具体执行步骤,一条流水线必须属于一个车间,一个车间的工作过程就是一个进程,车间负责把资源整合到一起,是一个资源单位,而车间内至少有一条流水线,也就是说一个进程至少有一个线程。

进程是一个资源单位,线程是CPU的最小执行单位。

线程和进程的关系:进程包含了运行程序需要的所有资源,每一个进程一旦被创建,就默认开启了一条线程,称之为主线程。一个进程可以包含多个线程,而线程依赖进程。

2.什么是多线程?

多线程指的是在一个进程中存在多个线程,多个线程共享该进程的地址空间,相当于一个车间内有多条流水线,都公用一个车间的资源。

注:多进程之间内存地址是相互隔离的,多线程之间的资源是共享的。

3.为什么使用多线程?

因为多进程对操作系统的资源耗费非常高,每开一个进程都要申请内存空间,而开线程,就相当于在一个车间内造一条流水线,无需申请空间,对操作系统的资源耗费比较小。多线程可以使CPU在一个进程内进行切换,从而提到CPU的占用率,从而提高程序的效率。

应用场景:当遇到I/O操作时,就可以使用多线程。

进程和线程的区别:

1.进程对于操作系统的资源耗费非常高,而线程相反(比进程低10-100倍)。

2.在同一个进程中,多个线程之间资源共享的;多个进程之间,内存是相互隔离的,即资源不共享。

4.开启线程的两种方式

1.实例化Thread类的对象

from threading import Thread

def task():
    print('子线程。。。')

t = Thread(target=task)
t.start()
print('')

# 子线程。。。  # 由于开启线程的速度非常快,所以有可能的情况就是子线程一经开启,就立马执行其中代码
#

2.继承Thread类,覆盖run方法

class MyThread(Thread):
    def run(self):
        print("子线程 running....")
MyThread().start()
print("over2")

5.多进程和多线程对比

1.开启速度

from threading import Thread
import time
def task():
    pass
start = time.time()
l = []
for i in range(100):
    t = Thread(target=task)
    l.append(t)
    t.start()
print(time.time()-start)
for t in l:
    t.join()
print('')

# 0.015770673751831055
#
开启多个线程
from multiprocessing import Process
import time
def task():
    pass

l = []
if __name__ == '__main__':
    start = time.time()
    for i in range(100):
        p = Process(target=task)
        l.append(p)
        p.start()
    print(time.time()-start)
    for p in l:
        p.join()
    print('')

# 3.595005750656128
#
开启多个进程

2.PID

from multiprocessing import Process
import time
import os

def task1():
    print(os.getpid())  # 9528
def task2():
    print(os.getpid())  # 11704


if __name__ == '__main__':
    p1 = Process(target=task1)
    p1.start()
    p2 = Process(target=task2)
    p2.start()
多进程的PID
from threading import Thread
import time
import os

def task1():
    print(os.getpid())  # 7560
def task2():
    print(os.getpid())  # 7560


if __name__ == '__main__':
    t1 = Thread(target=task1)
    t1.start()
    t2 = Thread(target=task2)
    t2.start()
多线程的PID

3.数据共享

from threading import Thread

x = 100
def task():
    global x
    x = 0
l = []
for i in range(100):
    t = Thread(target=task)
    t.start()
    l.append(t)
for t in l:
    t.join()
print(x)
print('over')


# 0
# over
多线程间数据共享
from multiprocessing import Process

x = 100
def task():
    global x
    x = 0
l = []
if __name__ == '__main__':

    for i in range(100):
        t = Process(target=task)
        t.start()
        l.append(t)
    for t in l:
        t.join()
    print(x)
    print('over')

# 100
# over
进程间数据数据不共享

6.线程相关的其他方法

Thread实例对象的方法
  # isAlive(): 返回线程是否活动的。
  # getName(): 返回线程名。
  # setName(): 设置线程名。

threading模块提供的一些方法:
  # threading.currentThread(): 返回当前的线程变量。
  # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
from threading import Thread
import threading
from multiprocessing import Process
import os

def work():
    import time
    time.sleep(3)
    print(threading.current_thread().getName())


if __name__ == '__main__':
    #在主进程下开启线程
    t=Thread(target=work)
    t.start()

    print(threading.current_thread().getName())
    print(threading.current_thread()) #主线程
    print(threading.enumerate()) #连同主线程在内有两个运行的线程
    print(threading.active_count())
    print('主线程/主进程')

    '''
    打印结果:
    MainThread
    <_MainThread(MainThread, started 140735268892672)>
    [<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>]
    主线程/主进程
    Thread-1
    '''
View Code

7.守护线程

守护线程会在所有非守护线程结束后结束。主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时进行回收),因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。

from threading import Thread
import time


def task():
    print('子线程 run....')
    time.sleep(0.1)
    print('子线程 over...')


t = Thread(target=task)
t.daemon = True  # 必须在start前
t.start()
print('主线程 over...')

# 子线程 run....
# 主线程 over...
from threading import Thread
import time
def task1():
    print('子线程1 run....')
    time.sleep(0.1)
    print('子线程1 over...')
def task2():
    print('子线程2 run....')
    # time.sleep(1)
    print('子线程2 over...')

t1 = Thread(target=task1)
t2 = Thread(target=task2)
t1.daemon = True  # 必须在start前
t1.start()
t2.start()
print('主线程 over...')

# 子线程1 run....
# 子线程2 run....
# 子线程2 over...
# 主线程 over...
例2

8.线程互斥锁

当多个线程需要同时修改同一份数据时,可能会造成数据错乱,所以这时必须加锁。

from threading import Thread,Lock
import time
lock = Lock()
x = 100
def task():
    lock.acquire()
    global x
    t = x - 1
    time.sleep(0.01)
    x = t
    lock.release()

l = []
for i in range(100):

    t = Thread(target=task)
    t.start()
    l.append(t)
for t in l:
    t.join()
print(x)
print('over')


# 0
# over
线程互斥锁

信号量:也是一种锁,其特点是可以设置一个数据可以被几个线程(进程)共享,即可以让这个数据在同一时间能被多个线程使用。它的应用场景是可以限制一个数据被同时访问的次数,保证程序正常运行。

from threading import Thread,Semaphore,current_thread
import time
sem = Semaphore(4)  # 其中的参数可以设置有几个线程可以同时共享数据
def task():
    sem.acquire()
    print('%s run...' % current_thread())
    time.sleep(2)
    sem.release()

for i in range(10):
    t = Thread(target=task)
    t.start()

线程互斥锁与信号量图解

http://url.cn/5DMsS9r
原文地址:https://www.cnblogs.com/wangke0917/p/10209175.html