精华内容
下载资源
问答
  • Linux线程挂掉是否影响进程

    千次阅读 多人点赞 2017-04-02 13:39:22
    如果没有设置对应的Signal Handler操作系统就自动终止进程(或者说默认的Signal Handler就是终止进程);如果设置了,理论上可以恢复进程状态继续跑(用longjmp之类的工具) 线程有自己的 stack,但是没有...
    严格的说没有“线程崩溃”,只是触发了SIGSEGV (Segmentation Violation/Fault)。如果没有设置对应的Signal Handler操作系统就自动终止进程(或者说默认的Signal Handler就是终止进程);如果设置了,理论上可以恢复进程状态继续跑(用longjmp之类的工具)

    线程有自己的 stack,但是没有单独的 heap,也没有单独的 address space。只有进程有自己的 address space,而这个 space 中经过合法申请的部分叫做 process space。Process space 之外的地址都是非法地址。当一个线程向非法地址读取或者写入,无法确认这个操作是否会影响同一进程中的其它线程,所以只能是整个进程一起崩溃。


    1.进程(主线程)创建了多个线程,多个子线程均拥有自己独立的栈空间(存储函数参数、局部变量等),但是多个子线程和主线程共享堆、全局变量等非栈内存。
    2.如果子线程的崩溃是由于自己的一亩三分地引起的,那就不会对主线程和其他子线程产生影响,但是如果子线程的崩溃是因为对共享区域造成了破坏,那么大家就一起崩溃了。3.举个栗子:主线程是一节车厢的乘务员,诸多乘客(也就是子线程)就是经过乘务员(主线程)检票确定可以进入车厢的,也就是主线程创建了诸多子线程,每个子线程有自己独立的区域(座位啊啥的),但是诸多乘客和乘务员共享走廊啊卫生间啊等等,如果其中一名乘客座位坏了,摔了(可以认为奔溃了),那么其他乘客和乘务员都不受影响,但是如果乘客将卫生间给破坏了,他也无法使用卫生间(崩溃了),其他乘客和乘务员也不能用卫生间,好吧,那么大家一起憋着吧(崩溃了)。

    总体来说,线程没有独立的地址空间,如果崩溃,会发信号,如果没有错误处理的handler,OS一般直接杀死进程。就算是有handler了处理,一般也会导致程序崩溃,因为很有可能其他线程或者进程的数据被破坏了。





    展开全文
  • 进程

    万次阅读 2021-05-25 14:29:00
    文章目录进程一.什么是多任务二、实现多任务的方式:示例1 一般情况下执行唱歌、跳舞示例2 单线程执行函数示例3 多线程执行函数示例4多线程执行唱歌、跳舞1示例5多线程执行唱歌、跳舞2三、什么是进程1.什么是进程?2...

    进程

    一.什么是多任务

    现代的操作系统(Windows,Mac OS X,Linux,UNIX等)都支持“多任务”。
    什么叫做多任务:操作系统同时运行多个任务

    • 单核CPU实现多任务原理:操作系统轮流让各个任务交替执行,QQ执行2us,切换到微信,再执行2us,再切换到陌陌,执行2us······。表面上是每个任务反复执行下去,但是CPU调度执行速度太快了,导致我们感觉就像所有任务都在同时执行一样。

    • 多核CPU实现多任务原理:真正的并行执行多任务只能在多核CPU上实现,但是由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

    并发:看上去一起执行,任务数多于CPU核心数
    并行:真正一起执行,任务数小于等于CPU核心数

    二、实现多任务的方式:

    1.多进程模式(使用较多)
    2.多线程模式(使用最多)
    3.协程模式 (使用较少)
    4.多进程+多线程模式 (容易出错)

    生活中与很多场景是同时进行的,比如开车的时候手和脚是同时来控制车。还有一些歌手边唱歌边跳舞。
    我们可以理解为我们的大脑控制着我们的身体进行不同的动作。大脑就是cpu,身体的所有支配动作用到的部位为我们的资源。
    试想下如果我们的大脑只能控制一个动作完成后才能控制下一个动作,也就是说我们只能先唱完歌再去跳舞,这是不是很尴尬呢?

    示例1 一般情况下执行唱歌、跳舞

    from time import sleep 
    def sing():
        for i in range(3):
            print('我正在唱歌。。。%d'%i)
            sleep(1)
    
    def dance():
        for i in range(3):
            print('我正在跳舞。。。%d'%i)
            sleep(1)
    if __name__ == '__main__':
        sing() # 先唱歌
        dance() # 再跳舞
    '''
    我正在唱歌。。。0
    我正在唱歌。。。1
    我正在唱歌。。。2
    我正在跳舞。。。0
    我正在跳舞。。。1
    我正在跳舞。。。2'''
    
    

    示例2 单线程执行函数

    from time import sleep
    # 单线程执行
    def saySorry():
        print("亲爱的,圣诞节快乐!")
        sleep(1)
    
    if __name__ == "__main__":
        for i in range(5):
            saySorry()
    """
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    """
    
    

    示例3 多线程执行函数

    import threading
    from time import sleep
    # 多线程执行
    def saySorry():
        print("亲爱的,圣诞节快乐!")
        sleep(1)
    
    if __name__ == "__main__":
        for i in range(5):
            t = threading.Thread(target=saySorry)
            t.start()  # 启动线程,即让线程开始执行
    """
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    # 亲爱的,圣诞节快乐!
    """
    
    

    示例4多线程执行唱歌、跳舞1

    import threading
    from time import sleep,ctime
    # 多线程执行唱歌跳舞的多任务。
    def sing():
        for i in range(3):
            print("正在唱歌...%d"%i)
            sleep(1)
    
    def dance():
        for i in range(3):
            print("正在跳舞...%d"%i)
            sleep(1)
    
    if __name__ == '__main__':
        print('---开始---:%s'%ctime())
    
        t1 = threading.Thread(target=sing)
        t2 = threading.Thread(target=dance)
    
        t1.start()
        t2.start()
    
        sleep(5)  # 屏蔽此行代码,试试看,程序是否会立马结束?
        print('---结束---:%s'%ctime())
    
    '''
    ---开始---:Tue May 25 11:26:19 2021
    正在唱歌...0
    正在跳舞...0
    正在唱歌...1
    正在跳舞...1
    正在唱歌...2
    正在跳舞...2
    ---结束---:Tue May 25 11:26:24 2021
    '''
    
    

    示例5多线程执行唱歌、跳舞2

    import threading
    from time import sleep,ctime
    # 多线程执行唱歌跳舞的多任务。
    def sing():
        for i in range(3):
            print("正在唱歌...%d"%i)
            sleep(1)
    
    def dance():
        for i in range(3):
            print("正在跳舞...%d"%i)
            sleep(1)
    
    if __name__ == '__main__':
        print('---开始---:%s'%ctime())
    
        t1 = threading.Thread(target=sing)
        t2 = threading.Thread(target=dance)
    
        t1.start()
        t2.start()
    
        # sleep(5) # 屏蔽此行代码,试试看,程序是否会立马结束?
        print('---结束---:%s'%ctime())
    
    '''---开始---:Tue May 25 11:19:25 2021
    正在唱歌...0
    正在跳舞...0---结束---:Tue May 25 11:19:25 2021
    
    正在唱歌...1
    正在跳舞...1
    正在唱歌...2
    正在跳舞...2'''
    
    

    三、什么是进程

    1.什么是进程?

    对于操作系统而言,一个任务就是一个进程,一个应用就是一个进程。
    程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念,而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
    进程是系统中程序执行和资源分配的基本单位,每个进程都有自己的数据段、代码段、和堆栈段。
    进程是一个具有一定独立功能的程序,一个实体,每一个进程都有它自己的地址空间。

    2.进程的状态

    进程执行时的间断性,决定了进程可能具有多种状态,事实上,运行中的进程具有以下三种基本状态。
    (1)就绪状态(Ready)
    (2)运行状态(Running)
    (3)阻塞状态(Blocked)

    3.单任务现象

    # 单任务现象
    from time import sleep
    
    def run():
        while True:
            print("haiyan is my wife")
            sleep(2)
    
    if __name__ == "__main__":
        while True:
            print("yangyu is a good man")
            sleep(1)
    
        run()    # 执行不了.因为上面的循环没有结束。
    '''
    执行结果:不停打印
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    yangyu is a good man
    ...
    '''
    
    

    4.启动进程实现多任务

    # 启动进程实现多任务
    """
    multiprocessing 库
    跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象
    """
    from multiprocessing import Process
    import os
    from time import sleep
    
    
    # 子进程需要执行的代码
    def run(str1):
        while True:
            # os.getpid()获取当前进程的id号
            # os.getppid()获取当前父进程的id号
            print("yangyu is a %s man--%s--%s" % (str1, os.getpid(), os.getppid()))
            sleep(1.2)
    
    
    if __name__ == "__main__":
        print("主(父)进程启动-%s" % (os.getpid()))
        # 创建子进程
        # target说明进程执行的任务
        p = Process(target=run, args=("nice",))
        # 启动进程
        p.start()
    
        while True:
            print("yangyu is a good man")
            sleep(1)
    '''
    主(父)进程启动-2372
    yangyu is a good man
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a nice man--5664--2372
    yangyu is a good man
    yangyu is a good man
    yangyu is a nice man--5664--2372'''
    
    

    5.父子进程的先后顺序

    # 父子进程的先后顺序
    from multiprocessing import Process
    from time import sleep
    
    def run(str1,str2):
        print("子进程启动+%s"%str1)
        sleep(3)
        print("子进程结束+%s"%str2)
    
    if __name__ == "__main__":
        print("父进程启动")
    
        p = Process(target=run, args=("start","end"))
        p.start()
    
        # 父进程的结束不能影响子进程,让父进程等待子进程的结束再执行父进程
        p.join()
        print("父进程结束")
    '''
    父进程启动
    子进程启动+start
    子进程结束+end
    父进程结束
    '''
    
    

    6.多个进程不能共享全局变量

    # 多个进程不能共享全局变量
    # 在子进程中修改全局变量对父进程中的全局变量没有影响
    # 在创建子进程时对全局变量做了一个备份,父进程中的子进程中的num是完全不同的两个变量
    from multiprocessing import Process
    num = 100
    def run():
        print("子进程开始")
        global num  # num = 100
        num += 1
        print(num)
        print("子进程结束")
    
    if __name__ == "__main__":
        print("父进程开始")
        p = Process(target=run)
        p.start()
        p.join()
        print("父进程结束--%d"%num)
    '''
    父进程开始
    子进程开始
    101
    子进程结束
    父进程结束--100
    '''
    
    

    7.启动多个子进程

    # 启动多个子进程
    from multiprocessing import Pool
    import os,time
    def run(name):
        print("子进程%d启动--%s" % (name, os.getpid()))
        start = time.time()
        time.sleep(3)
        end = time.time()
        print("子进程%d结束--%s--耗时%.2f" % (name, os.getpid(), end-start))
    
    if __name__ == "__main__":
        t1 = time.time()
        print("父进程启动")
        # 创建多个进程
        # 进程池
        # 表示可以同时执行的进程数量
        # Pool默认大小是cpu核心线程数,我的笔记本电脑是2核4线程(这里的线程就同时执行的进程),Pool()默认为4,默认同时执行4进程,总耗时为18.2s;改为2,总耗时为32.5s;改为8,总耗时14.1s;改为20,总耗时20.35s。所有一般就用核心数*线程数(2*4=8),执行速度最快。
        pp = Pool(8)
        for i in range(20):
            # 创建进程,放入进程池同意管理
            pp.apply_async(run, args=(i,))
        # 在调用join之前必须先调用close,调用close之后就不能再继续添加新的进程了
        pp.close()
        # 进程池对象调用join,会等待进程池中所有的子进程结束完毕再去执行父进程
        pp.join()
        print("父进程结束,总耗时为%s"%(time.time()-t1))
    '''
    父进程启动
    子进程0启动--9048
    子进程1启动--3648
    子进程2启动--7180
    子进程3启动--7888
    子进程4启动--8228
    子进程5启动--8664
    子进程6启动--8688
    子进程7启动--9432
    子进程0结束--9048--耗时3.01
    子进程8启动--9048
    子进程1结束--3648--耗时3.01
    子进程9启动--3648
    子进程2结束--7180--耗时3.01
    子进程10启动--7180
    子进程3结束--7888--耗时3.01
    子进程11启动--7888
    子进程4结束--8228--耗时3.01
    子进程12启动--8228
    子进程5结束--8664--耗时3.01
    子进程13启动--8664
    子进程6结束--8688--耗时3.01
    子进程14启动--8688
    子进程7结束--9432--耗时3.01
    子进程15启动--9432
    子进程8结束--9048--耗时3.07
    子进程16启动--9048
    子进程9结束--3648--耗时3.01
    子进程17启动--3648
    子进程10结束--7180--耗时3.01
    子进程18启动--7180
    子进程11结束--7888--耗时3.01
    子进程19启动--7888
    子进程12结束--8228--耗时3.01
    子进程13结束--8664--耗时3.01
    子进程14结束--8688--耗时3.01
    子进程15结束--9432--耗时3.01
    子进程16结束--9048--耗时3.01
    子进程17结束--3648--耗时3.01
    子进程18结束--7180--耗时3.04
    子进程19结束--7888--耗时3.01
    父进程结束,总耗时为14.196025133132935
    '''
    
    

    8.多进程文件拷贝

    1.普通文件拷贝

    # 实现文件的拷贝
    import os, time
    def copyFile(rPath, wPath):
        fr = open(rPath, "rb")
        fw = open(wPath, "wb")
        context = fr.read()
        fw.write(context)
        fr.close()
        fw.close()
    
    path = r"F:\PycharmProjects\Project\进程\file1\1905热门电影图片"
    toPath = r"F:\PycharmProjects\Project\进程\file2"
    
    # 读取path下的所有文件
    fileList = os.listdir(path)
    
    # 启动for循环去处理每个文件
    start = time.time()
    for fileName in fileList:
        copyFile(os.path.join(path,fileName), os.path.join(toPath, fileName))
    end = time.time()
    print("总耗时:%0.2f" % (end-start))  # 总耗时:14.68
    

    2.多进程文件拷贝

    import os, time
    from multiprocessing import Pool
    # 实现文件的拷贝
    def copyFile(rPath, wPath):
        fr = open(rPath, "rb")
        fw = open(wPath, "wb")
        context = fr.read()
        fw.write(context)
        fr.close()
        fw.close()
    
    path = r"F:\PycharmProjects\Project\进程\file1\1905热门电影图片"
    toPath = r"F:\PycharmProjects\Project\进程\file2"
    
    if __name__ == "__main__":
        # 读取path下的所有文件
        fileList = os.listdir(path)
        start = time.time()
        pp = Pool(4)
        for fileName in fileList:
            pp.apply_async(copyFile, args=(os.path.join(path, fileName), os.path.join(toPath, fileName)))
        pp.close()
        pp.join()
        end = time.time()
        print("总耗时:%0.2f" % (end - start))  # 总耗时:11.40
    

    9.封装进程对象

    1.创建yangyuProcess.py文件

    from multiprocessing import Process
    import os, time
    
    class YangyuProcess(Process):
        def __init__(self, name):
            Process.__init__(self)
            self.name = name
    
        def run(self):
            print("子进程(%s-%s)启动" % (self.name, os.getpid()))
    
            # 子进程的功能
            time.sleep(3)
    
            print("子进程(%s-%s)结束" % (self.name, os.getpid()))
    

    2.from yangyuProcess import YangyuProcess

    from yangyuProcess import YangyuProcess
    
    if __name__ == "__main__":
        print("父进程启动")
        # 创建子进程
        p = YangyuProcess("test")
        # 自动调用p进程对象的run方法
        p.start()
        p.join()
        print("父进程结束")
    '''
    打印结果:
    父进程启动
    子进程(test-3476)启动
    子进程(test-3476)结束
    父进程结束
    '''
    """
    这样写的好处:子进程的方法不用和父进程的方法写在一起,让主程序结构更清晰。
    """
    

    10.进程间的通信

    1.示例1

    from multiprocessing import Process, Queue
    import os,time
    
    def write(q):
        print("启动写子进程%s"%(os.getpid()))
        for chr in ["A","B","C","D"]:
            q.put(chr)
            time.sleep(1)
        print("结束写子进程%s"%(os.getpid()))
    
    def read(q):
        print("启动读子进程%s" % (os.getpid()))
        while True:
            value = q.get(True)
            print("value=" + value)
        print("结束读子进程%s" % (os.getpid()))
    
    
    if __name__ == "__main__":
        # 父进程创建队列,并传给子进程
        q = Queue()
        pw = Process(target=write, args=(q,))
        pr = Process(target=read, args=(q,))
    
        pw.start()
        pr.start()
    
        pw.join()
        pr.terminate()  # pr进程里是个死循环,无法等待其结束,只能强制结束
    
        print("父进程结束")
    
    '''
    启动写子进程7752
    启动读子进程7264
    value=A
    value=B
    value=C
    value=D
    结束写子进程7752
    父进程结束'''
    
    

    2.示例2

    from multiprocessing import Process, Queue
    import os,time
    
    def write(q):
        print("启动写子进程%s"%(os.getpid()))
        for chr in ["A","B","C","D"]:
            q.put(chr)
            print(chr)
        print("结束写子进程%s"%(os.getpid()))
    
    def read(q):
        print("启动读子进程%s" % (os.getpid()))
        while not q.empty():
            value = q.get(True)
            print("value=" + value)
            time.sleep(1)
        print("结束读子进程%s" % (os.getpid()))
    
    
    if __name__ == "__main__":
        print("父进程开始")
        # 父进程创建队列,并传给子进程
        q = Queue(4)
        pw = Process(target=write, args=(q,))
        pr = Process(target=read, args=(q,))
    
        pw.start()
        pr.start()
        pw.join()
        pr.join()
        print("父进程结束")
    
    '''
    父进程开始
    启动写子进程8228
    A
    B
    C
    D
    结束写子进程8228
    启动读子进程7472
    value=A
    value=B
    value=C
    value=D
    结束读子进程7472
    父进程结束
    '''
    
    展开全文
  • 浅谈操作系统的进程通信部分对于数据库的影响进程间的通信共享内存的实现共享内存与Oracle SGA 最近在温习操作系统相关的基本原理,结合以前的数据库实践,就简单说一说最基础的操作系统进程通信部分对数据库的一些...

    浅谈操作系统的进程通信对于数据库的影响

    最近在温习操作系统相关的基本原理,结合以前的数据库实践,就简单说一说最基础的操作系统进程通信部分对数据库的一些影响。

    进程间的通信

    操作系统内的进程在执行时可以分为独立的或者是协作的。
    独立进程的含义是该进程不能影响其他进程或受其他进程影响,反之,则是协作的进程。
    协作进程的意义通常在于以下理由:
    1、加速计算:这也是计算机发展到多个处理内核和进行进程间调度的原因,充分利用cpu资源以达到加快程序处理的作用。如果需要一个进程能够快速计算,那么应将其拆分为多个子程序或者说是子任务,在多处理器内核上并行执行。
    2、模块化的要求:在一个计算机系统中,可能会需要用模块化的方式构造系统,会将系统功能拆分成独立的进程或线程,这些进程或线程共同组成了一个完整的系统。
    3、信息的共享:多用户系统中,由于多个用户可能需要访问同样的信息,所以就需要提供一套信息共享的环境来允许这些用户并发的访问这些信息。
    4、多任务的并行要求:从小处说是进程需要并行执行来提高效率,从大处来说,用户可以并行的进行编写代码、听音乐等工作。

    那进程之间要如何进行协作?这里就需要提到IPC(进程间通信:InterProcess Communication)机制,以便进程间交互信息。
    进程之间的通信有两种模式:共享内存、管道通信和消息传递。
    消息传递这种模式适合于交换较小数量的数据,消息传递有个缺点是它的实现经常要采用系统调用,所以就需要更多的时间来让内核介入到进程的处理中,因此在一个分布式的系统中,消息传递的模式要比共享内存更容易实现。
    共享内存的模式,需要通信进程建立共享内存区域来实现信息的共享,在建立共享内存区域时采用系统调用,而一旦建立好该区域,对于该区域的访问就无需内核介入了,直接读取,不需要拷贝数据。
    这里多说一嘴,对于多个处理内核的系统,消息传递的模式要优于内存共享的模式,虽然共享内存是最快的IPC模式。但在共享内存的模式中,共享数据会在多个高速缓存之间迁移,会有高速缓存一致性的问题。对于多个进程对共享内存区域的并发读写问题,共享内存模式并没有提供同步的机制,所以引入了信号量的机制来实现对共享内存的同步访问控制。这里如果熟悉数据库或分布式系统的人很容易就会联想到其中的读写一致性问题。

    共享内存的实现

    在Linux中,每个进程都有属于自己的进程控制块(PCB:进程实体的一部分,记录了操作系统所需要的、用于描述进程和控制进程运行的全部信息)和地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射(逻辑关联),通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一块区域,它们所指向的这块区域即共享内存,这种方法称为内存映射(memory mapping)文件。
    在这里插入图片描述

    共享内存与Oracle SGA

    SGA是orace中的一个巨大的内存共享区域,在linux中,其设置与系统内核参数shamax有关。
    shamax参数的作用是系统允许的单个共享内存段的最大值,如果这个参数远小于oracle sga的大小,那么SGA可能被分配成很多个共享内存段。
    在Oracle的使用中,我们一般不希望Oracle的共享内存段跨区,我们希望共享内存是在一个段中或者减少共享内存段的数量,来减少碎片的产生和提升一些特定场景下的性能,所以我们会建议调整shamax参数来限制共享内存段的数量。
    在linux中,我们可以通过ipcs命令来管理共享内存。
    查看:
    在这里插入图片描述

    关于共享内存段的题外话:大家可能会在某些场景下碰到共享内存段没有释放导致的oracel无法启动的问题,这时就需要手动释放这些共享内存。

    展开全文
  • 进程与线程

    万次阅读 多人点赞 2021-03-17 22:50:20
    进程是系统中独立存在的实体,它可以拥有自己独立的资源,每个进程都拥有自己私有的地址空间,在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间 动态性 进程与程序的区别在于,程序只是一...

    进程与线程

    1 进程

    1.1 进程的概念

    进程就是正在运行的程序,它会占用对应的内存区域,由CPU进行执行与计算。

    1.2 进程的特点

    • 独立性
      进程是系统中独立存在的实体,它可以拥有自己独立的资源,每个进程都拥有自己私有的地址空间,在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间
    • 动态性
      进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合,程序加入了时间的概念以后,称为进程,具有自己的生命周期和各种不同的状态,这些概念都是程序所不具备的.
    • 并发性
      多个进程可以在单个处理器CPU上并发执行,多个进程之间不会互相影响.

    2 线程

    2.1 线程的概念

    线程是操作系统OS能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.
    一个进程可以开启多个线程,其中有一个主线程来调用本进程中的其他线程。
    我们看到的进程的切换,切换的也是不同进程的主线程
    多线程可以让同一个进程同时并发处理多个任务,相当于扩展了进程的功能。

    2.2 进程与线程的关系

    一个操作系统中可以有多个进程,一个进程中可以包含一个线程(单线程程序),也可以包含多个线程(多线程程序)
    进程与线程的关系
    每个线程在共享同一个进程中的内存的同时,又有自己独立的内存空间.
    所以想使用线程技术,得先有进程,进程的创建是OS操作系统来创建的,一般都是C或者C++完成
    进程与线程的关系

    3 多线程的特性

    3.1 随机性

    我们宏观上觉得多个进程是同时运行的,但实际的微观层面上,一个CPU【单核】只能执行一个进程中的一个线程。
    那为什么看起来像是多个进程同时执行呢?
    是因为CPU以纳秒级别甚至是更快的速度高效切换着,超过了人的反应速度,这使得各个进程从看起来是同时进行的,也就是说,宏观层面上,所有的进程看似并行【同时运行】,但是微观层面上是串行的【同一时刻,一个CPU只能处理一件事】。
    线程切换

    串行与并行

    串行是指同一时刻一个CPU只能处理一件事,类似于单车道
    并行是指同一时刻多个CPU可以处理多件事,类似于多车道
    在这里插入图片描述
    在这里插入图片描述

    3.2 CPU分时调度

    时间片,即CPU分配给各个线程的一个时间段,称作它的时间片,即该线程被允许运行的时间,如果在时间片用完时线程还在执行,那CPU将被剥夺并分配给另一个线程,将当前线程挂起,如果线程在时间片用完之前阻塞或结束,则CPU当即进行切换,从而避免CPU资源浪费,当再次切换到之前挂起的线程,恢复现场,继续执行。
    注意:我们无法控制OS选择执行哪些线程,OS底层有自己规则,如:

    1. FCFS(First Come First Service 先来先服务算法)
    2. SJS(Short Job Service短服务算法)

    CPU分片

    3.3 线程的状态

    由于线程状态比较复杂,我们由易到难,先学习线程的三种基础状态及其转换,简称”三态模型” :

    • 就绪(可运行)状态:线程已经准备好运行,只要获得CPU,就可立即执行
    • 执行(运行)状态:线程已经获得CPU,其程序正在运行的状态
    • 阻塞状态:正在运行的线程由于某些事件(I/O请求等)暂时无法执行的状态,即线程执行阻塞
      线程的3种状态

    就绪 → 执行:为就绪线程分配CPU即可变为执行状态"
    执行 → 就绪:正在执行的线程由于时间片用完被剥夺CPU暂停执行,就变为就绪状态
    执行 → 阻塞:由于发生某事件,使正在执行的线程受阻,无法执行,则由执行变为阻塞
    (例如线程正在访问临界资源,而资源正在被其他线程访问)
    反之,如果获得了之前需要的资源,则由阻塞变为就绪状态,等待分配CPU再次执行

    我们可以再添加两种状态:

    • 创建状态:线程的创建比较复杂,需要先申请PCB,然后为该线程运行分配必须的资源,并将该线程转为就绪状态插入到就绪队列中
    • 终止状态:等待OS进行善后处理,最后将PCB清零,并将PCB返回给系统
      线程的5种状态

    PCB(Process Control Block):为了保证参与并发执行的每个线程都能独立运行,OS配置了特有的数据结构PCB来描述线程的基本情况和活动过程,进而控制和管理线程

    3.4 线程状态与代码对照

    线程状态与代码对照
    线程生命周期,主要有五种状态:

    1. 新建状态(New) : 当线程对象创建后就进入了新建状态.如:Thread t = new MyThread();
    2. 就绪状态(Runnable):当调用线程对象的start()方法,线程即为进入就绪状态.
      处于就绪(可运行)状态的线程,只是说明线程已经做好准备,随时等待CPU调度执行,并不是执行了t.start()此线程立即就会执行
    3. 运行状态(Running):当CPU调度了处于就绪状态的线程时,此线程才是真正的执行,即进入到运行状态
      就绪状态是进入运行状态的唯一入口,也就是线程想要进入运行状态状态执行,先得处于就绪状态
    4. 阻塞状态(Blocked):处于运状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会被CPU选中再次执行.
      根据阻塞状态产生的原因不同,阻塞状态又可以细分成三种:
      等待阻塞:运行状态中的线程执行wait()方法,本线程进入到等待阻塞状态
      同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态
      其他阻塞:调用线程的sleep()或者join()或发出了I/O请求时,线程会进入到阻塞状态.当sleep()状态超时.join()等待线程终止或者超时或者I/O处理完毕时线程重新转入就绪状态
    5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期

    4 多线程代码创建方式1:继承Thread

    4.1 概述

    Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例
    启动线程的唯一方法就是通过Thread类的start()实例方法
    start()方法是一native方法,它将通知底层操作系统,.最终由操作系统启动一个新线程,操作系统将执行run()
    这种方式实现的多线程很简单,通过自己的类直接extends Thread,并重写run()方法,就可以自动启动新线程并执行自己定义的run()方法
    模拟开启多个线程,每个线程调用run()方法.

    4.2 常用方法

    构造方法

    Thread() 分配新的Thread对象
    Thread(String name) 分配新的Thread对象
    Thread(Runnable target) 分配新的Thread对象
    Thread(Runnable target,String name) 分配新的Thread对象

    普通方法

    static Thread currentThread( )
    返回对当前正在执行的线程对象的引用
    long getId()
    返回该线程的标识
    String getName()
    返回该线程的名称
    void run()
    如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法
    static void sleep(long millions)
    在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
    void start()
    使该线程开始执行:Java虚拟机调用该线程的run()

    4.3 测试多线程的创建方式1

    创建包: cn.tedu.thread
    创建类: TestThread1.java

    package cn.tedu.thread;
    /*本类用于多线程编程实现方案一:继承Thread类来完成*/
    public class TestThread1 {
        public static void main(String[] args) {
            //4.创建线程对象进行测试
            /*4.new对应的是线程的新建状态
            * 5.要想模拟多线程,至少得启动2个线程,如果只启动1个,是单线程程序*/
            MyThread t1 = new MyThread();
            MyThread t2 = new MyThread();
            MyThread t3 = new MyThread();
            MyThread t4 = new MyThread();
            /*6.这个run()如果直接这样调用,是没有多线程抢占执行的效果的
            * 只是把这两句话看作普通方法的调用,谁先写,就先执行谁*/
            //t1.run();
            //t2.run();
            /*7.start()对应的状态就是就绪状态,会把刚刚新建好的线程加入到就绪队列之中
            * 至于什么时候执行,就是多线程执行的效果,需要等待OS选中分配CPU
            * 8.执行的时候start()底层会自动调用我们重写的run()种的业务
            * 9.线程的执行具有随机性,也就是说t1-t4具体怎么执行
            * 取决于CPU的调度时间片的分配,我们是决定不了的*/
            t1.start();//以多线程的方式启动线程1,将当前线程变为就绪状态
            t2.start();//以多线程的方式启动线程2,将当前线程变为就绪状态
            t3.start();//以多线程的方式启动线程3,将当前线程变为就绪状态
            t4.start();//以多线程的方式启动线程4,将当前线程变为就绪状态
        }
    }
    
    //1.自定义一个多线程类,然后让这个类继承Thread
    class MyThread extends Thread{
        /*1.多线程编程实现的方案1:通过继承Thread类并重写run()来完成的 */
        //2.重写run(),run()里是我们自己的业务
        @Override
        public void run() {
            /*2.super.run()表示的是调用父类的业务,我们现在要用自己的业务,所以注释掉*/
            //super.run();
            //3.完成业务:打印10次当前正在执行的线程的名称
            for (int i = 0; i < 10; i++) {
                /*3.getName()表示可以获取当前正在执行的线程名称
                * 由于本类继承了Thread类,所以可以直接使用这个方法*/
                System.out.println(i+"="+getName());
            }
        }
    }
    

    5 多线程代码创建方式2:实现Runnable接口

    5.1 概述

    如果自己的类已经extends另一个类,就无法多继承,此时,可以实现一个Runnable接口

    5.2 常用方法

    void run()使用实现接口Runnable的对象创建线程时,启动该线程将导致在独立执行的线程中调用对象的run()方法

    5.3 练习2:测试多线程的创建方式2

    创建包: cn.tedu.thread
    创建类: Thread2.java

    package cn.tedu.thread;
    /*本类用于多线程编程实现方案二:实现Runnable接口来完成*/
    public class TestThread2 {
        public static void main(String[] args) {
            //5.创建自定义类的对象--目标业务类对象
            MyRunnable target = new MyRunnable();
            //6.如何启动线程?自己没有,需要与Thread建立关系
            Thread t1 = new Thread(target);
            Thread t2 = new Thread(target);
            Thread t3 = new Thread(target);
            Thread t4 = new Thread(target);
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }
    
    //1.自定义多线程类
    class MyRunnable implements Runnable{
        //2.添加父接口中的抽象方法run(),里面是自己的业务
        @Override
        public void run() {
            //3.写业务,打印10次当前正在执行的线程名称
            for (int i = 0; i < 10; i++) {
                /*问题:自定义类与父接口Runnable中都没有获取名字的方法
                * 所以还需要从Thread中找:
                * currentThread():静态方法,获取当前正在执行的线程对象
                * getName():获取当前线程的名称*/
                System.out.println(i+"="+Thread.currentThread().getName());
            }
        }
    }
    

    5.4 两种实现方式的比较

    • 继承Thread类
      优点: 编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this即可获得当前线程
      缺点: 自定义的线程类已继承了Thread类,所以后续无法再继承其他的类
    • 实现Runnable接口
      优点: 自定义的线程类只是实现了Runnable接口或Callable接口,后续还可以继承其他类,在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码、还有数据分开(解耦),形成清晰的模型,较好地体现了面向对象的思想
      缺点: 编程稍微复杂,如想访问当前线程,则需使用Thread.currentThread()方法

    6 售票案例

    需求:设计4个售票窗口,总计售票100张。用多线程的程序设计并写出代码

    6.1 方案1:继承Thread

    创建包: cn.tedu.tickets
    创建类: TestThread.java

    package cn.tedu.tickets;
    /*需求:设计多线程编程模型,4个窗口共计售票100张
    * 本方案使用多线程编程方案1,继承Thread类的方式来完成*/
    public class TestThread {
        public static void main(String[] args) {
            //5.创建多个线程对象
            TicketThread t1 = new TicketThread();
            TicketThread t2 = new TicketThread();
            TicketThread t3 = new TicketThread();
            TicketThread t4 = new TicketThread();
            //6.以多线程的方式启动
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }
    
    //1.自定义多线程售票类,继承Thread
    class TicketThread extends Thread{
        //3.定义变量,保存要售卖的票数
        /*问题:4个线程对象共计售票400张,原因是创建了4次对象,各自操作各自的成员变量
        * 解决:让所有对象共享同一个数据,票数需要设置为静态*/
        static int tickets = 100;
        //2.重写父类的run(),里面是我们的业务
        @Override
        public void run() {
            //4.1循环卖票
            while(true){
                try {
                    //7.让每个线程经历休眠,增加线程状态切换的频率与出错的概率
                    //问题1:产生了重卖的现象:同一张票卖了多个人
                    //问题2:产生了超卖的现象:超出了规定的票数100,出现了0 -1 -2这样的票
                    Thread.sleep(10);//让当前线程休眠10ms
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //4.2打印当前正在卖票的线程名称,并且票数-1
                System.out.println(getName()+"="+tickets--);
                //4.3做判断,如果没有票了,就退出死循环
                if(tickets <= 0) break;//注意,死循环一定要设置出口
            }
        }
    }
    

    6.2 方案2:实现Runnable

    创建包: cn.tedu.tickets
    创建类: TestRunnable.java

    package cn.tedu.tickets;
    /*需求:设计多线程编程模型,4个窗口共计售票100张
     * 本方案使用多线程编程方案2,实现Runnable接口的方式来完成*/
    public class TestRunnable {
        public static void main(String[] args) {
            //5.创建Runnable接口的实现类对象,作为目标业务对象
            TicketRunnable target = new TicketRunnable();
            //6.创建多个Thread类线程对象,并将target业务对象交给多个线程对象来处理
            Thread t1 = new Thread(target);
            Thread t2 = new Thread(target);
            Thread t3 = new Thread(target);
            Thread t4 = new Thread(target);
            //7.以多线程的方式启动多个线程对象
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }
    
    //1.自定义多线程类实现Runnable接口
    class TicketRunnable implements Runnable{
        //3.定义一个成员变量,用来保存票数100
        /*由于自定义类对象只创建了一次,所以票数被所有线程对象Thread类的对象共享*/
        int tickets = 100;
        //2.添加接口中未实现的方法,方法里是我们的业务
        @Override
        public void run() {
            //4.1循环卖票
            while(true){
                //8.让线程休眠10ms,增加线程状态切换的概率和出错的概率
                try {
                    Thread.sleep(10);//让当前线程休眠10ms
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //4.2打印当前正在售票的线程名称 & 票数-1
                System.out.println(Thread.currentThread().getName()+"="+tickets--);
                //4.3设置死循环的出口,没票了就停止卖票
                if(tickets <=0 ) break;
            }
        }
    }
    

    6.3 问题

    1. 每次创建线程对象,都会生成一个tickets变量值是100,创建4次对象就生成了400张票了。不符合需求,怎么解决呢?能不能把tickets变量在每个对象间共享,就保证多少个对象都是卖这100张票。
      解决方案: 用静态修饰
    2. 产生超卖,0 张 、-1张、-2张。
    3. 产生重卖,同一张票卖给多人。
    4. 多线程安全问题是如何出现的?常见情况是由于线程的随机性+访问延迟。
    5. 以后如何判断程序有没有线程安全问题?
      在多线程程序中 + 有共享数据 + 多条语句操作共享数据
      解决方案:下一节 同步锁点这里
    展开全文
  • 如题,大神,如何在当前进程调用system,而又不影响当前进程的正常运行!3q!
  • 进程和线程的区别(超详细)

    万次阅读 多人点赞 2019-10-03 21:57:46
    进程和线程 进程 一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。 线程 进程中的一个执行任务(控制单元),负责...
  • 而这个新创建出来的进程被称为原进程的子进程,原进程被称为该进程的父进程。 该函数其实是一个系统调用接口,原型如下: #include <unistd.h> pid_t fork(void); 特性:子进程会复制父进程的PCB,二者之间...
  • 当一个进程调用fork时,因为子进程在开始时复制父进程的存储映像,信号捕捉函数的地址在子进程中是有意义的,所以子进程继承父进程的信号处理方式。 特殊的是exec,因为exec运行新的程序后会覆盖从父进程继承来的...
  • 当一个进程调用fork时,因为子进程在开始时复制父进程的存储映像,信号捕捉函数的地址在子进程中是有意义的,所以子进程继承父进程的信号处理方式。  但是当子进程调用exec后,因为exec运行新的程序后会覆盖从父...
  • 进程基础

    千次阅读 多人点赞 2019-11-03 20:07:32
    进程的基本概念 程序顺序执行的特征: 1)顺序性:处理机严格按照程序所规定的顺序执行,每一步操作必须在下一步操作开始前执行 2)封闭性:程序在封闭的环境下运行,程序独占资源,资源的状态由程序决定,...
  • 进程调度

    千次阅读 2012-12-04 15:27:35
    进程调度 无论是在批处理系统还是分时系统中,用户进程数一般都多于处理机数、这将导致它们互相争夺处理机。另外,系统进程也同样需要使用处理机。这就要求进程调度程序按一定的策略,动态地把处理机分配给处于...
  • 进程管理

    千次阅读 多人点赞 2021-05-05 22:15:07
    进程管理
  • Linux线程挂掉对整个进程影响

    千次阅读 2020-04-14 17:45:24
    如果没有设置对应的Signal Handler操作系统就自动终止进程(或者说默认的Signal Handler就是终止进程);如果设置了,理论上可以恢复进程状态继续跑(用longjmp之类的工具) 线程有自己的 stack,但是没有单独的 ...
  • 72-孤儿进程与孤儿进程

    千次阅读 2017-02-26 15:02:51
    之前有学习过僵尸进程,它指的是“未老先衰”的子进程先于父进程去逝,而父进程未对它进行回收(wait)所产生的。本文来谈谈,另一个对立的概念——孤儿进程,以及孤儿进程组。1. 孤独进程如果一个进程,它的父进程先...
  • 一个进程被操作系统调入到内存中运行,其对应的进程在内存中分为代码区,数据区,堆区和栈区 1.数据区:存放全局,静态和常量 2.栈区:主要用于存放局部变量,传递参数,存放函数的返回地址 3.堆区:用户存放动态...
  • 昨天学了进程控制,就这三种特殊的进程研究了一下,其中也借鉴了一些前人总计的经验。 1、孤儿进程 2、僵尸进程 3、守护进程
  • 孤儿进程、僵尸进程、守护进程

    千次阅读 2016-08-04 19:58:50
    孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。僵尸进程:一个进程使用fork创建子进程...
  • 进程、线程和前台进程、后台进程

    千次阅读 2018-05-07 20:17:49
     进程进程:是正在执行的一个程序(软件)或者命令,每一个进程都是一个运行的实体都有自己的地址空间,并且占用一定的系统资源。说白了进程就是正在运行着的程序,只要运行就会产生至少一个进程。为什么说只要程序...
  • 守护进程

    千次阅读 2019-09-02 14:49:40
    什么是守护进程? Daemon(精灵)进程是Linux中后台服务进程,独立于控制终端并且周期性地执行某种任务或等待处理某些发生事件,一般采用以d结尾的名字。 守护进程就是通常讲Daemon进程,是linux后台执行的一种...
  • 进程和子进程进程和线程

    千次阅读 2017-08-25 16:03:13
    进程继承的来自父进程的属性: ● ● ● ● ● ● ● ●已打开的文件描述符 ●实际用户ID、实际组ID、有效用户ID、有效组ID ●附属组ID ●进程组ID ●会话ID
  • 进程进程组、会话以及守护进程

    千次阅读 2013-09-29 15:28:18
    1.进程进程组与会话关系及其特性。 (1) 每个进程都有一个进程ID唯一识别(pid_t pid=getpid()); (2) 每个进程都属于一个进程组(getpgrp()),进程组属于一个会话(getsid(getpid())),是子集关系,即包含与被...
  • 文章目录一、进程与PCB1. 进程的概念:2. 什么是PCBtask_structtask_ struct内容分类4. 查看进程5. 进程概念的加深二、父进程与子进程1. 通过系统调用获取进程标示符2. 进程的创建 - fork3. fork的深层探讨(关键)...
  • 答:首先得知道什么是进程什么是线程,我的理解是进程是指在系统中正在运行的一个应用程序;程序一旦运行就是进程,或者更专业化来说:进程是指程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集...
  • 父子进程终止顺序与僵死进程

    千次阅读 2015-11-09 21:23:26
    那么关于父子进程终止先后顺序又会有什么影响呢? 1、父进程在子进程之前终止 对于父进程已经终止的所有进程,它们的父进程都改变为init进程。我们称这些进程由init进程收养。其操作过程大致是:在一个进程终止时...
  • 我们在fork 创建子进程的时候,有个特点就是 主进程和子进程互不影响, 但是 我们在用Process 类创建的子进程就需要等待 子进程全部结束后然后再结束主进程。 下边我们验证两个问题: 主进程的和子进程之间数据隔离...
  • 文章目录一、概述二、进程组与守护进程1. 进程组2. 进程组操作函数3. 会话4. 守护进程 一、概述 二、进程组与守护进程 1. 进程进程组,也称之为作业。BSD于1980年前后向Unix中增加的一个新特性,代表一个或多个...
  • 1进程组 一个或过个进程的集合,进程组ID是一个正整数。用来获得当前进程组ID的函数。 pid_t getpgid(pid_t pid) ...组长进程可以创建一个进程组,创建该进程组中的进程,然后终止,只要进程组中有一个进程

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 650,452
精华内容 260,180
关键字:

影响进程