• 操作系统线程理论


    操作系统线程理论

    线程概念的引入背景

    进程

    ​ 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了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)>]
    主
    守护线程结束
    '''
    
  • 相关阅读:
    .NET题目(收集来自网络)
    c#反射
    c#泛型
    asp.net core 下载文件,上传excel文件
    remote: HTTP Basic: Access denied fatal: Authentication failed for'https'
    获取jwt(json web token)中存储的用户信息
    System.data.sqlclient.sqlexception:将截断字符串或二进制数据终止
    代码热更新
    左手坐标系 vs 右手坐标系
    GPU相关临时笔记
  • 原文地址:https://www.cnblogs.com/dadazunzhe/p/11543406.html
Copyright © 2020-2023  润新知