Java教程

进程与线程的相关操作

本文主要是介绍进程与线程的相关操作,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

内容概要

  • 僵尸进程与孤儿进程
  • 守护进程
  • 互斥锁(重点)
  • 消息队列
  • 实现进程间数据交互(IPC机制)
  • 生产者消费者模型
  • 线程理论

内容详细

一、进程补充

僵尸进程与孤儿进程

僵尸进程

主进程代码运行完成之后不会直接结束,而是要等待所有子进程运行完回收它们的资源之后才能结束。

孤儿进程

主进程已经死亡(非正常死亡),而子进程还在运行中。

守护进程

守护进程: 守护着某个进程,一但守护的进程结束,守护进程也会随之结束

from multiprocessing import Process
import time


def test():
    print('守护进程 is running')
    time.sleep(3)
    print('守护进程 is over')


if __name__ == '__main__':
    p = Process(target=test)
    # 把 p 进程设置为守护进程
    p.daemon = True
    p.start()
    time.sleep(0.2)
    print('被守护的主进程')

# 运行结果
守护进程 is running
被守护的主线程

# '守护进程 is over'不会执行,因为主进程结束后, p 进程也会结束

互斥锁

image

拿抢票举例,数据库中车票还剩一张,有一个购买功能,先用文件操作获取数据库中车票数量的数据,再进行减一运算再保存,如果是并发状态下执行该功能,可以有多个进程同时对数据修改成功,也就是有多个人同时购买同一张票成功了,造成数据安全问题

问题: 并发情况下操作同一份数据,极其容易造成数据错乱

解决措施:将并发变成串行,虽然降低了效率但是提升了数据的安全行性

  • 锁就可以实现将并发变成串行的效果
使用锁的注意事项
	在主进程中产生 交由子进程使用
	1.一定要在需要的地方加锁 千万不要随意加
    2.不要轻易的使用锁(死锁现象)
    # 在以后的编程生涯中 几乎不会解除到自己操作锁的情况
import json
from multiprocessing import Process, Lock
import time
import random


# 查票
def search(name):
    with open(r'data.txt', 'r', encoding='utf8') as f:
        data_dict = json.load(f)
        ticket_num = data_dict.get('ticket_num')
        print('%s查询余票:%s' % (name, ticket_num))


# 买票
def buy(name):
    # 先查票
    with open(r'data.txt', 'r', encoding='utf8') as f:
        data_dict = json.load(f)
        ticket_num = data_dict.get('ticket_num')
    # 模拟一个延迟
    time.sleep(random.random())
    # 判断是否有票
    if ticket_num > 0:
        # 将余票减一
        data_dict['ticket_num'] -= 1
        # 重新写入数据库
        with open(r'data.txt', 'w', encoding='utf8') as f:
            json.dump(data_dict, f)
        print('%s: 购买成功' % name)
    else:
        print('不好意思 没有票了!!!')


def run(name,mutex):
    search(name)
    mutex.acquire()  # 抢锁
    buy(name)
    mutex.release()  # 释放锁

if __name__ == '__main__':
    mutex = Lock()
    for i in range(1, 11):
        p = Process(target=run, args=('用户%s' % i,mutex))
        p.start()

消息队列

队列:先进先出
 
from multiprocessing import Queue

# 这条命令就是产生一个队列对象 q
# q 可以调用 put() 方法往队列中添加数据,调用 get() 方法从队列中获取数据
q = Queue(5)  # 括号内可以填写最大等待数


# 存放数据
q.put(111)
q.put(222)
# print(q.full())  # False       判断队列中数据是否满了
q.put(333)
q.put(444)
q.put(555)
# print(q.full())
# q.put(666)  # 超出范围原地等待 直到有空缺位置
# 提取数据
print(q.get())
print(q.get())
print(q.get())
# print(q.empty())  # 判断队列中是否还存在数据
print(q.get())
print(q.get())
# print(q.get())  # 没有数据之后原地等待直到有数据为止
print(q.get_nowait())  # 没有数据立刻报错

"""
full和get_nowait能否用于多进程情况下的精确使用
    不能!!!
    因为这两个方法只能判断某一时间刻队列中的数据情况,但队列的数据是随时会有其它进程进行着改动的

队列的使用就可以打破进程间默认无法通信的情况
"""

IPC 机制

实现进程间的数据交互

原理:创建一个队列,所有进程都可以都可以对这个队列进行数据存储操作,有进程往队列中添加数据,别的进程就可以从中取出它存进去的数据,实现进程间的数据交互

疑问:难道创建一个列表,然后所有进程都操操作这个列表不可以实现同样的效果吗?当然不行,列表只能存在某一个进程的全局名称空间中,而进程之间默认无法进行数据交互,也就无法操作某个进程中的列表。

from multiprocessing import Queue, Process


def producer(q):
    q.put("子进程p放的数据")


def consumer(q):
    print('子进程c取的数据',q.get())


if __name__ == '__main__':
    q = Queue()
    p = Process(target=producer, args=(q,))
    c = Process(target=consumer, args=(q,))
    p.start()
    c.start()

    # q.put('主进程放的数据')
    # p = Process(target=consumer, args=(q,))
    # p.start()
    # p.join()
    # print(q.get())
    # print('主')

生产者消费者模型

image

也就是利用 IPC 机制,实现进程之的数据交互

"""
生产者
	负责产生数据(做包子的)
消费者
	负责处理数据(吃包子的)
该模型需要解决恭喜不平衡现象
"""
# JoinableQueue 产生的队列可以提供更多队列操作方法,如 task_done() 可以判断队列中的数据是否被获取干净,没有数据则不会再执行 get() 方法

from multiprocessing import Queue, Process, JoinableQueue
import time
import random


def producer(name, food, q):
    for i in range(10):
        print('%s 生产了 %s' % (name, food))
        q.put(food)
        time.sleep(random.random())


def consumer(name, q):
    while True:
        data = q.get()
        print('%s 吃了 %s' % (name, data))
        # 判断队列中的数据是否被获取干净,没有数据则不会再执行 get() 方法
        q.task_done()


if __name__ == '__main__':
    # q = Queue()
    q = JoinableQueue()
    p1 = Process(target=producer, args=('大厨jason', '玛莎拉', q))
    p2 = Process(target=producer, args=('印度阿三', '飞饼', q))
    p3 = Process(target=producer, args=('泰国阿人', '榴莲', q))
    c1 = Process(target=consumer, args=('班长阿飞', q))

    p1.start()
    p2.start()
    p3.start()
    # 主进程执行结束之后会立刻结束
    c1.daemon = True
    c1.start()
	# 主进程等待所有生产者生产完数据
    p1.join()
    p2.join()
    p3.join()
	# 主进程等待队列中所有数据被取干净
    q.join()  # 等待队列中所有的数据被取干净

    print('主')

二、线程

线程理论

什么是线程?

进程其实是一个资源单位,真正被CPU执行的其实是进程里面的线程

"""
进程就像工厂,线程就像工厂中的一条条流水线
所有进程肯定含有最少一个线程
"""

进程间的数据默认是隔离的,但同一个进程中的多个线程之间的数据是可以共享的

开设线程的两种方式

"""
开设进程需要做哪些操作
	1.重新申请一块内存空间
	2.将所需的资源全部导入
开设线程需要做哪些操作
	上述两个步骤都不需要 所以开设线程消耗的资源远比开设进程的少!!!
"""
from threading import Thread
import time

# 直接开启线程
def test(name):
    print('%s is running' % name)
    time.sleep(3)
    print('%s is over' % name)


t = Thread(target=test, args=('jason',))
t.start()
print('主')


# 用类开设线程
class MyClass(Thread):
    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):
        print('%s is running' % self.name)
        time.sleep(3)
        print('%s is over' % self.name)

obj = MyClass('jason')
obj.start()
print('主线程')

线程对象的其它方法

1.join方法
2.获取进程号(验证同一个进程内可以开设多个线程)
3.active_count统计当前正在活跃的线程数
4.current_thread

守护线程

"""
主线程的结束意味着整个进程的结束
	所以主线程需要等待里面所有非守护线程的结束才能结束
"""
from threading import Thread
from multiprocessing import Process
import time
def foo():
    print(123)
    time.sleep(3)
    print("end123")
def bar():
    print(456)
    time.sleep(1)
    print("end456")
if __name__ == '__main__':
    t1=Thread(target=foo)
    t2=Thread(target=bar)
    t1.daemon=True
    t1.start()
    t2.start()
    print("main-------")

线程数据共享

from threading import Thread
money = 100

def test():
    global money
    money = 999

t = Thread(target=test)
t.start()
t.join()
print(money)

# 执行结果
999
说明主进程与子进程之间数据共享

线程互斥锁

from threading import Thread, Lock
from multiprocessing import Lock
import time


num = 100


def test(mutex):
    global num
    mutex.acquire()
    # 先获取num的数值
    tmp = num
    # 模拟延迟效果
    time.sleep(0.1)
    # 修改数值
    tmp -= 1
    num = tmp
    mutex.release()

t_list = []
mutex = Lock()
for i in range(100):
    t = Thread(target=test, args=(mutex,))
    t.start()
    t_list.append(t)
# 确保所有的子线程全部结束
for t in t_list:
    t.join()
print(num)

TCP 服务实现并发

开启多线程

import socket
from threading import Thread
from multiprocessing import Process

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)


def talk(sock):
    while True:
        try:
            data = sock.recv(1024)
            if len(data) == 0: break
            print(data.decode('utf8'))
            sock.send(data + b'gun dan!')
        except ConnectionResetError as e:
            print(e)
            break
    sock.close()


while True:
    sock, addr = server.accept()
    print(addr)
    # 开设多进程或者多线程
    t = Thread(target=talk, args=(sock,))
    t.start()

train

1.自行百度搜索什么是"乐观锁与悲观锁"
2.自行百度搜索什么是"消息队列"
3.自行百度搜索什么是"二十三种设计模式"
# 平时就可以练习算法(冒泡 二分 插入 快排 堆排 桶排)
	笔纸写出来的:冒泡 二分 插入 快排
    能够描述出内部原理的:堆排 桶排
这篇关于进程与线程的相关操作的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!