并发编程十二问

1、简述计算机操作系统中的“中断”的作用?

计算机操作系统的中断的作用:cpu会切:io阻塞,程序运行时间过长

    中断:计算机执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得
cpu暂时中断当前正在执行的程序而转去执行相应的事件处理程序。

    待处理完毕后又返回原来被中断处理急需执行或者调度新的进程执行的过程,它使计算
机可以更好更快的利用有限的系统资源解决系统响应速度和运行效率的一种控制技术:
    实时响应 + 系统调用

    中断装置是由一些特定的寄存器和控制线路组成,中央处理器和外围设备等识别到的
事件保存在特定的寄存器中。
    中央处理器每执行完一条指令,均有中断装置判别是否有事件发生。
    若无事件发生,CPU继续执行。
    若有事件发生,则中断装置中断原占有CPU的程序的执行,让操作系统的处理事件服
务程序占用CPU,对出现的事件进行处理,事件处理完后,再让原来的程序继续占用CPU执行

2、简述计算机内存中的“内核态”和“用户态”;

操作系统的核心是内核,独立于普通的应用程序,内核可以访问受保护的内存空间,
也可以访问底层硬件设备的所有权限,为了保证用户进程不能直接操作内核,保证内核
的安全,操作系统将虚拟空间划分为两部分,一部分是内核空间,一部分是用户空间。

    内核态:运行操作系统的程序,os的数据存放

    用户态:运行用户程序,用户进程的数据存放

    用户态的应用程序可以通过三种方式来访问内核态的资源:
        1)系统调用
        2)库函数
        3)Shell脚本
    用户态到内核态的切换:
        1.系统调用        用户程序主动发起的 软中断 os.fork() process
        2.异常            被动的   当CPU正在执行运行在用户态的程序时,突然发生某些预
先不可知的异常事件,这个时候就会触发从当前用户态执行的进程转向内核态执行相关的
异常事件,典型的如缺页异常。

        3.外围设备的硬中断  被动的   外围设备完成用户的请求操作后,会像CPU发出中断信号,
此时,CPU就会暂停执行下一条即将要执行的指令,转而去执行中断信号对应的处理程序,
如果先前执行的指令是在用户态下,则自然就发生从用户态到内核态的转换。

3、进程间通信方式有哪些?

进程间通信(IPC)
    消息队列(    队列 = 管道 + 锁)
    管道(使用消息传递的)
    有名管道(FIFO)
    信号量
    共享内存
    套接字(socket)

4、简述你对管道、队列的理解;

管道通常指无名管道
1、它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端
2、它只能用于具有亲缘关系的进程中通信(也就是父与子进程或者兄弟进程之间)
3、数据不可反复读取了,即读了之后欢喜红区中就没有了
消息队列
1、消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
2、消息队列独立于发送与接收进程。进程终止时,消息队列及其内容不会被删除。
3、消息队列可以实现消息随机查询。

    队列 = 管道 + 锁

5、请列举你知道的进程间通信方式;

队列,信号量,Event事件,定时器Timer,线程queue,进程池线程池,异步调用+回调机制

6、什么是同步I/O,什么是异步I/O?

同步IO指的是同步传输 ,当发送一个数据请求时,会一直等待,直到有返回结果为止

        异步IO指的是异步传输 ,当发送一个数据请求时,会立即去处理别的事情,当有数据
处理完毕后,会自动的返回结果

    一般同步传输能保证数据正确性 ,而异步能最大化性能。
    如给u盘复制一个大的数据文件,你开了缓冲优化,是异步 工作, 复制的快了,
    你要是刚复制完了直接拔 会丢数据,
     你要是关了,复制的慢了,但你要是关了缓冲优化,复制完了直接拔 不会丢数据,

异步IO
    用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方
面,从kernel的角度,当它受到一个asynchronous read之后,首先它会
立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据
准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给
用户进程发送一个signal,告诉它read操作完成了。

7、请问multiprocessing模块中的Value、Array类的作用是什么?举例说明它们的使用场景

通常,进程之间彼此是完全孤立的,唯一的通信方式是队列或者管道,但是可以使用两个对象来表示共享数据。其实这些对象使用了共享内存(通过mmap模块)使访问多个进程成为可能。

Value( typecode, arg1, … argN, lock ) 

    在共享内容中常见ctypes对象。typecode要么是包含array模块使用的相同类型代码
(如’i’,’d’等)的字符串,要么是来自ctypes模块的类型对象(如ctypes.c_int、
ctypes.c_double等)。
    所有额外的位置参数arg1, arg2 ….. argN将传递给指定类型的构造函数。lock是只能
使用关键字调用的参数,如果把它置为True(默认值),将创建一个新的锁定来包含对值的访问。
    如果传入一个现有锁定,比如Lock或RLock实例,该锁定将用于进行同步。如果v是Value创建
的共享值的实例,便可使用v.value访问底层的值。例如,读取v.value将获取值,而赋值v.value
将修改值。

  RawValue( typecode, arg1, … ,argN)
同Value对象,但不存在锁定。

  Array( typecode, initializer, lock )
    在共享内存中创建ctypes数组。typecode描述了数组的内容,意义与Value()函数中的相同。
initializer要么是设置数组初始大小的整数,要么是项目序列,其值和大小用于初始化数组。
lock是只能使用关键字调用的参数,意义与Value()函数中相同。
    如果a是Array创建的共享数组的实例,便可使用标准的python索引、切片和迭代操作访问它
的内容,其中每种操作均由锁定进行同步。对于字节字符串,a还具有a.value属性,可以吧整个
数组当做一个字符串进行访问。

  RawArray(typecode, initializer )
    同Array对象,但不存在锁定。当所编写的程序必须一次性操作大量的数组项时,如果同时
使用这种数据类型和用于同步的单独锁定(如果需要的话),性能将得到极大的提升。

应该注意,使用多进程后,通常不必再担心与锁定、信号量或类似构造的底层同步,这一点与线程不相伯仲。在某种程度上,管道上的send()和receive()操作,以及队列上的put()和get()操作已经提供了同步功能。但是,在某写特定的设置下还是需要用到共享值和锁定。下面这个例子说明了如何使用共享数组代替管道,将一个浮点数的python列表发送给另一个进程:

import multiprocessing
class FloatChannel(object):
    def __init__(self,maxsize):
        self.buffer=multiprocessing.RawArray(‘d‘,maxsize)
        self.buffer_len=multiprocessing.Value(‘i‘)
        self.empty=multiprocessing.Semaphore(1)
        self.full=multiprocessing.Semaphore(0)
    def send(self,values):
        self.empty.acquire()  #只在缓存为空时继续
        nitems=len(values)
        self.buffer_len=nitems  #设置缓冲区大小
        self.buffer[:nitems]=values #将复制到缓冲区中
        self.full.release() #发信号通知缓冲区已满
    def recv(self):
        self.full.acquire()     #只在缓冲区已满时继续
        values=self.buffer[:self.buffer_len.value]  #复制值
        self.empty.release()        #发送信号通知缓冲区为空
        return values
    #性能测试 接收多条消息
def consume_test(count,ch):
    for i in xrange(count):
        values=ch.recv()

#性能测试 发送多条消息
def produce_test(count,values,ch):
    for i in xrange(count):
        ch.send(values)
if __name__=="__main__":
    ch=FloatChannel(100000)
    p=multiprocessing.Process(target=consume_test,args=(1000,ch))
    p.start()
    values=[float(x) for x in xrange(100000)]
    produce_test(1000,values,ch)
    print "Done"
    p.join()

8、请问multiprocessing模块中的Manager类的作用是什么?与Value和Array类相比,Manager的优缺点是什么?

可以通过使用Value或者Array把数据存储在一个共享的内存表中;Manager()返回一个manager类型,控制一个server process,可以允许其它进程通过代理复制一些python objects   支持list,dict,Namespace,Lock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value,Array ;

Manager类的作用共享资源,manger的的优点是可以在poor进程池中使用,缺点是windows下环境下性能比较差,因为windows平台需要把Manager.list放在if name =‘ main ‘下,而在实例化子进程时,必须把Manager对象传递给子进程,否则lists无法被共享,而这个过程会消耗巨大资源,因此性能很差。

multiprocessing 是一个使用方法类似threading模块的进程模块。允许程序员做并行开发。并且可以在UNIX和Windows下运行。

通过创建一个Process 类型并且通过调用call()方法spawn一个进程。

一个比较简单的例子:

from multiprocessing import Process
import time
def f(name):
time.sleep(1)
print ‘hello ‘,name
print os.getppid() #取得父进程ID
print os.getpid()  #取得进程ID
process_list = []
if __name__ == ‘__main__‘:
for i in range(10):
p = Process(target=f,args=(i,))
p.start()
process_list.append(p)
for j in process_list:
j.join()

进程间通信:

有两种主要的方式:Queue、Pipe

1- Queue类几乎就是Queue.Queue的复制,示例:

from multiprocessing import Process,Queue
import time
def f(name):
time.sleep(1)
q.put([‘hello‘+str(name)])
process_list = []
q = Queue()
if __name__ == ‘__main__‘:
for i in range(10):
p = Process(target=f,args=(i,))
p.start()
process_list.append(p)
for j in process_list:
j.join()
for i in range(10):
print q.get()

2- Pipe 管道

from multiprocessing import Process,Pipe
import time
import os

def f(conn,name):
time.sleep(1)
conn.send([‘hello‘+str(name)])
print os.getppid(),‘-----------‘,os.getpid()
process_list = []
parent_conn,child_conn = Pipe()
if __name__ == ‘__main__‘:
for i in range(10):
p = Process(target=f,args=(child_conn,i))
p.start()
process_list.append(p)
for j in process_list:
j.join()
for p in range(10):
print parent_conn.recv()

Pipe()返回两个连接类,代表两个方向。如果两个进程在管道的两边同时读或同时写,会有可能造成corruption.

进程间同步

multiprocessing contains equivalents of all the synchronization primitives from threading.

例如,可以加一个锁,以使某一时刻只有一个进程print

from multiprocessing import Process,Lock
import time
import os

def f(name):
lock.acquire()
time.sleep(1)
print ‘hello--‘+str(name)
print os.getppid(),‘-----------‘,os.getpid()
lock.release()
process_list = []
lock = Lock()
if __name__ == ‘__main__‘:
for i in range(10):
p = Process(target=f,args=(i,))
p.start()
process_list.append(p)
for j in process_list:
j.join()

进程间共享状态 Sharing state between processes

当然尽最大可能防止使用共享状态,但最终有可能会使用到.

1-共享内存

可以通过使用Value或者Array把数据存储在一个共享的内存表中

from multiprocessing import Process,Value,Array
import time
import os

def f(n,a,name):
time.sleep(1)
n.value = name * name
for i in range(len(a)):
a[i] = -i
process_list = []
if __name__ == ‘__main__‘:
num = Value(‘d‘,0.0)
arr = Array(‘i‘,range(10))
for i in range(10):
p = Process(target=f,args=(num,arr,i))
p.start()
process_list.append(p)
for j in process_list:
j.join()
print num.value
print arr[:]
输出:
[email protected]:~/projects$ python pp.py
81.0
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]

‘d‘和‘i‘参数是num和arr用来设置类型,d表示一个双精浮点类型,i表示一个带符号的整型。

更加灵活的共享内存可以使用multiprocessing.sharectypes模块

Server process

Manager()返回一个manager类型,控制一个server process,可以允许其它进程通过代理复制一些python objects

支持list,dict,Namespace,Lock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value,Array

from multiprocessing import Process,Manager
import time
import os

def f(d,name):
time.sleep(1)
d[name] = name * name
print d
process_list = []
if __name__ == ‘__main__‘:
manager = Manager()
d = manager.dict()
for i in range(10):
p = Process(target=f,args=(d,i))
p.start()
process_list.append(p)
for j in process_list:
j.join()
print d
输出结果:
{2: 4}
{2: 4, 3: 9}
{2: 4, 3: 9, 4: 16}
{1: 1, 2: 4, 3: 9, 4: 16}
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 8: 64}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

Server process managers比共享内存方法更加的灵活,一个单独的manager可以被同一网络的不同计算机的多个进程共享。比共享内存更加的缓慢

使用工作池Using a pool of workers

Pool类代表 a pool of worker processes.

It has methods which allows tasks to be offloaded to the worker processes in a few different ways.

9、写一个程序,包含十个线程,子线程必须等待主线程sleep 10秒钟之后才执行,并打印当前时间;

# _*_ coding: utf-8 _*_
# 写一个程序,包含十个线程,子线程必须等待主线程sleep 10秒钟
# 之后才执行,并打印当前时间
from threading import Thread
import time

def task(name):
        print(name,time.strftime(‘%Y-%m-%d %H:%M:%S‘,time.localtime()))

if __name__ == ‘__main__‘:
    time.sleep(2)
    for i in range(10):
        t = Thread(target=task,args=(‘线程 %s‘%i,))
        t.start()

10、写一个程序,包含十个线程,同时只能有五个子线程并行执行;

# _*_ coding: utf-8 _*_
# 10、写一个程序,包含十个线程,同时只能有五个子线程并行执行;
from threading import Thread
from threading import currentThread
from concurrent.futures import ThreadPoolExecutor
import time
import random

def task():
    print(currentThread().getName())
    time.sleep(random.randint(1,3))

if __name__ == ‘__main__‘:
    pool = ThreadPoolExecutor(5)
    for i in range(10):
        pool.submit(task)

11、写一个程序,要求用户输入用户名和密码,要求密码长度不少于6个字符,且必须以字母开头,如果密码合法,则将该密码使用md5算法加密后的十六进制概要值存入名为password.txt的文件,超过三次不合法则退出程序;

# _*_ coding: utf-8 _*_
# 11、写一个程序,要求用户输入用户名和密码,要求密码
# 长度不少于6个字符,且必须以字母开头,如果密码合法,
# 则将该密码使用md5算法加密后的十六进制概要值存入名
# 为password.txt的文件,超过三次不合法则退出程序;
import re
import hashlib
import pickle

def func():
    count = 0
    while count<3:
        username = input("username>>>").strip()
        password = input("password>>>").strip()
        if len(password) >=6 and re.search(‘\A([a-z)|[A-Z])‘,password):
            md5_password =  hashlib.md5(password.encode(‘utf-8‘)).hexdigest()
            file_obj = {‘username‘:username,
                        ‘passworf‘:md5_password}
            f = open(‘password.txt‘,‘ab‘)
            pickle.dump(file_obj,f)
            break
        else:
            print("请输入合法的密码")
            count +=1
    else:
        print("您的机会已经用完")
if __name__ == ‘__main__‘:
    func()

12、写一个程序,使用socketserver模块,实现一个支持同时处理多个客户端请求的服务器,要求每次启动一个新线程处理客户端请求;

服务端:

# _*_ coding: utf-8 _*_
# 12、写一个程序,使用socketserver模块,
# 实现一个支持同时处理多个客户端请求的服务器,
# 要求每次启动一个新线程处理客户端请求;

import socketserver

class Handler(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            try:
                data = self.request.recv(1024)
                if not data:
                    break
                print(‘client data‘,data.decode())
                self.request.send(data.upper())
            except Exception as e:
                print(e)
                break
if __name__ == ‘__main__‘:
    server = socketserver.ThreadingTCPServer((‘127.0.0.1‘,8888),Handler)
    server.serve_forever()

客户端

# _*_ coding: utf-8 _*_
import socket
ip_port = (‘127.0.0.1‘,8888)
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(ip_port)
while True:
    msg = input(">>>>").strip()
    if not msg:
        continue
    client.send(msg.encode(‘utf-8‘))
    data = client.recv(1024)
    print(data)

原文地址:https://www.cnblogs.com/lfs2640666960/p/9581622.html

时间: 2025-01-08 22:17:55

并发编程十二问的相关文章

Win32 Windows编程 十二

一.对话框 1.对话框的分类 2.对话框的基本使用方式 3.对话框资源 4.有模式对话框的使用 5. 无模式对话框的使用 5.1 添加对话框资源 5.2 定义窗口处理函数 BOOL CALLBACK DialogProc( HWND hWnd, UINT UMsg, WPARAM wParam, LPARAM lParam ) 5.3 创建对话框 HWND CreateDialog( HINSTANCE hInstance, // handle to module LPCTSTR lpTempl

并发编程(二):全视角解析volatile

一.目录 1.引入话题-发散思考 2.volatile深度解析 3.解决volatile原子性问题 4.volatile应用场景 二.引入话题-发散思考 public class T1 { /*volatile*/ boolean running=true; public void m(){ System.out.println(Thread.currentThread().getName()+":start!"); while(running){ /*try { TimeUnit.M

java高并发编程(二)

马士兵java并发编程的代码,照抄过来,做个记录. 一.分析下面面试题 /** * 曾经的面试题:(淘宝?) * 实现一个容器,提供两个方法,add,size * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束 * * 分析下面这个程序,能完成这个功能吗? * @author mashibing */ package yxxy.c_019; import java.util.ArrayList; import java.util.List

(转)《深入理解java虚拟机》学习笔记10——并发编程(二)

Java的并发编程是依赖虚拟机内存模型的三个特性实现的: (1).原子性(Atomicity): 原子性是指不可再分的最小操作指令,即单条机器指令,原子性操作任意时刻只能有一个线程,因此是线程安全的. Java内存模型中通过read.load.assign.use.store和write这6个操作保证变量的原子性操作. long和double这两个64位长度的数据类型java虚拟机并没有强制规定他们的read.load.store和write操作的原子性,即所谓的非原子性协定,但是目前的各种商业

并发编程实践二:AbstractQueuedSynchronizer

AbstractQueuedSynchronizer,简称AQS,是java.util.concurrent包的synchronizer的基础框架,其它的synchronizer(包括Lock.Semaphore.CountDownLatch.FutureTask等)都是以它作为基础构建的,这篇文章我将对AQS的框架结构作出介绍,包括它对同步状态的管理,功能流程,等待队列的管理等,并涉及到一些实现的细节,但这里不涉及原理性的东西,原理将放到后面具体的实现类中去讲述. 这片文章采用从整体到局部的方

聊聊高并发(十二)分析java.util.concurrent.atomic.AtomicStampedReference源码来看如何解决CAS的ABA问题

在聊聊高并发(十一)实现几种自旋锁(五)中使用了java.util.concurrent.atomic.AtomicStampedReference原子变量指向工作队列的队尾,为何使用AtomicStampedReference原子变量而不是使用AtomicReference是因为这个实现中等待队列的同一个节点具备不同的状态,而同一个节点会多次进出工作队列,这就有可能出现出现ABA问题. 熟悉并发编程的同学应该知道CAS操作存在ABA问题.我们先看下CAS操作. CAS(Compare and

Java多线程和并发(十二),Java线程池

目录 1.利用Executors创建线程的五种不同方式 2.为什么要使用线程池 3.Executor的框架 4.J.U.C的三个Executor接口 5.ThreadPoolExecutor 6.线程池的状态 7.线程池大小如何选定 十二.Java线程池 1.利用Executors创建线程的五种不同方式 2.为什么要使用线程池 3.Executor的框架 4.J.U.C的三个Executor接口 5.ThreadPoolExecutor 虽然Executor提供的五种方法够用了,但是仍然不能满足

Java 并发编程(二)对象的不变性和安全的公布对象

一.不变性 满足同步需求的还有一种方法是使用不可变对象(Immutable Object). 到眼下为止,我们介绍了很多与原子性和可见性相关的问题,比如得到失效数据.丢失更新操作或光查到某个对象处于不一致的状态等等,都与多线程视图同一时候訪问同一个可变的状态相关.假设对象的状态不会改变,那么这些问题与复杂性也就自然消失了. 假设某个对象在被创建后其状态就不能被改动,那么这个对象就被成为不可变对象.线程安全型是不可变对象的固有属性之中的一个,他们的不变性条件是由构造函数创建的,仅仅要他们的状态不改

Java 并发编程(二)对象的不变性和安全的发布对象

一.不变性 满足同步需求的另一种方法是使用不可变对象(Immutable Object).到目前为止,我们介绍了许多与原子性和可见性相关的问题,例如得到失效数据,丢失更新操作或光查到某个对象处于不一致的状态等等,都与多线程视图同时访问同一个可变的状态相关.如果对象的状态不会改变,那么这些问题与复杂性也就自然消失了. 如果某个对象在被创建后其状态就不能被修改,那么这个对象就被成为不可变对象.线程安全型是不可变对象的固有属性之一,他们的不变性条件是由构造函数创建的,只要他们的状态不改变,那么这些不变