操作系统线程理论

操作系统线程理论

线程概念的引入背景

进程

​ 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

有了进程为什么要有线程

​ 进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:

  • 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
  • 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

  如果这两个缺点理解比较困难的话,举个现实的例子也许你就清楚了:如果把我们上课的过程看成一个进程的话,那么我们要做的是耳朵听老师讲课,手上还要记笔记,脑子还要思考问题,这样才能高效的完成听课的任务。而如果只提供进程这个机制的话,上面这三件事将不能同时执行,同一时间只能做一件事,听的时候就不能记笔记,也不能用脑子思考,这是其一;如果老师在黑板上写演算过程,我们开始记笔记,而老师突然有一步推不下去了,阻塞住了,他在那边思考着,而我们呢,也不能干其他事,即使你想趁此时思考一下刚才没听懂的一个问题都不行,这是其二。

  现在你应该明白了进程的缺陷了,而解决的办法很简单,我们完全可以让听、写、思三个独立的过程,并行起来,这样很明显可以提高听课的效率。而实际的操作系统中,也同样引入了这种类似的机制——线程。

线程的出现

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

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

  注意:进程是资源分配的最小单位,线程是CPU调度的最小单位.

     每一个进程中至少有一个线程。 

进程和线程的关系

线程与进程的区别可以归纳为以下4点:

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

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

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

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

大白话描述进程与线程

在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程,cpu真正的执行单位是线程.
在工厂中,  每个车间都有房子,而且每个车间默认就有一条流水线.

操作系统 ===> 工厂
进程 ===> 车间
线程 ===> 流水线
cpu  ===> 电源

线程:操作系统调度的最小单位
进程:资源集合/资源单位(最小的内存分配单位)
线程运行 = 运行代码
进程运行 = 各种资源 + 线程

线程与进程的区别
    线程==》单指代码的执行过程
    进程==》资源的申请与销毁的过程

    进程内存空间彼此隔离
    同一个进程下的线程共享资源.

    进程和线程的创建速度
        进程需要申请资源开辟空间 慢
        线程只是告诉操作系统一个执行方案 快

threading模块

线程的创建

# 第一种
from threading import Thread
import time
def func(name):
    print('线程 start')
    time.sleep(2)
    print('线程 end')
    
if __name__ == '__main__':
    t = Thread(target=task)
    t.start()
    print('主进程')

# 第二种
from threading import Thread
import time
# 进程等待所有线程结束才会结束

class Mythread(Thread):
    def run(self):
        print('子线程 start')45
        time.sleep(5)
        print('子线程 end')

t = Mythread()
t.start()
print('主线程')

多进程与多线程

pid的比较

from threading import Thread
from multiprocessing import Process
import os

def work():
    print('hello',os.getpid())

if __name__ == '__main__':
    #part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
    t1=Thread(target=work)
    t2=Thread(target=work)
    t1.start()
    t2.start()
    print('主线程/主进程pid',os.getpid())

    #part2:开多个进程,每个进程都有不同的pid
    p1=Process(target=work)
    p2=Process(target=work)
    p1.start()
    p2.start()
    print('主线程/主进程pid',os.getpid())

开启效率的较量

from threading import Thread
from multiprocessing import Process
import os

def work():
    print('hello')

if __name__ == '__main__':
    #在主进程下开启线程
    t=Thread(target=work)
    t.start()
    print('主线程/主进程')
    '''
    打印结果:
    hello
    主线程/主进程
    '''

    #在主进程下开启子进程
    t=Process(target=work)
    t.start()
    print('主线程/主进程')
    '''
    打印结果:
    主线程/主进程
    hello
    '''

内存数据的共享问题

from  threading import Thread
from multiprocessing import Process
import os
def work():
    global n
    n=0

if __name__ == '__main__':
    # n=100
    # p=Process(target=work)
    # p.start()
    # p.join()
    # print('主',n) #毫无疑问子进程p已经将自己的全局的n改成了0,但改的仅仅是它自己的,查看父进程的n仍然为100


    n=1
    t=Thread(target=work)
    t.start()
    t.join()
    print('主',n) #查看结果为0,因为同一进程内的线程之间共享进程内的数据
同一进程内的线程共享该进程的数据?

多线程实现socketserver

# server

import socket
from threading import Thread

sk = socket.socket()
sk.bind(('127.0.0.1', 8088))
sk.listen()

def func(conn):
    while True:
        res = conn.recv(1024).decode('utf8')
        print(res)
        conn.send(res.upper().encode('utf8'))
if __name__ == '__main__':
    while True:
        conn, addr = sk.accept()
        t = Thread(target=func,args=(conn,))
        t.start()

# client1

import socket

sk = socket.socket()
sk.connect(('127.0.0.1',8088))
while True:

    data = input('>>>')
    data = 'client1:'+data
    sk.send(data.encode('utf8'))
    res = sk.recv(1024).decode('utf8')
    print(res)

# client2

import socket

sk = socket.socket()
sk.connect(('127.0.0.1',8088))
while True:

    data = input('>>>')
    data = 'client2:'+data
    sk.send(data.encode('utf8'))
    res = sk.recv(1024).decode('utf8')
    print(res)

Thread类的其他方法

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
    '''

线程的join方法

from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print('%s say hello' %name)

if __name__ == '__main__':
    t=Thread(target=sayhi,args=('egon',))
    t.start()
    t.join()
    print('主线程')
    print(t.is_alive())
    '''
    egon say hello
    主线程
    False
    '''

守护线程

# 守护线程 守护的是进程的运行周期
from threading import Thread,enumerate,currentThread
import time

def task():
    print('守护线程开始')
    print(currentThread())
    time.sleep(5)
    print('守护线程结束')

def task2():
    print('子线程 start')
    time.sleep(3)
    print(currentThread())
    print('子线程 end')

if __name__ == '__main__':
    t1 = Thread(target=task)
    t2 = Thread(target=task2)
    t1.daemon = True
    t1.start()
    t2.start()

    time.sleep(5)
    print(enumerate())
    print('主')
'''
守护线程开始
<Thread(Thread-1, started daemon 1540)>
子线程 start
<Thread(Thread-2, started 14464)>
子线程 end
[<_MainThread(MainThread, started 14732)>, <Thread(Thread-1, started daemon 1540)>]
主
守护线程结束
'''
posted @ 2019-09-18 16:37  SweetJack  阅读(197)  评论(0编辑  收藏  举报