Python多任务,进程、线程、协程

多任务

线程

threading

threading可以使同时调用函数,从而实现多任务

import  time
import threading


def sing():
    """唱歌 5秒钟"""
    for i in range(5):
        print("....正在唱菊花茶....")
        time.sleep(1)


def dance():
    """跳舞 5秒钟"""
    for i in range(5):
        print("....正在跳舞....")
        time.sleep(1)


def main():
    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)
    t1.start()  # 启动线程,即让线程开始执行
    t2.start()


if __name__ == '__main__':
    main()

并行真的多任务

并发假的多任务

threading中的enumerate()函数显示所有线程的信息

如果创建Thread时执行的函数,运行结束那么意味着这个子线程结束了

当调用Thread的时候,不会创建线程;当调用Thread创建出来的实例对象的start方法地时候才会创建线程以及让这个线程开始运行


通过继承Thread类完成创建线程
import threading
import time


class MyThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm"+self.name+' @ '+str(i)  # name属性中保存的是当前线程的名字
            print(msg)


if __name__ == "__main__":
    t = MyThread()
    t.start()


多线程共享全局变量

在一个函数中对全局变量进行修改的时候,到底是否需要使用global进行说明要看是否对全局变量的执行指向进行了修改,如果修改了指向,即让全局变量指向了一个新的地方,那么必须使用global,如果仅仅是修改了指向的空间中的数据,此时不用必须使用global。


import threading
import time


# 定义一个全局变量
g_num = 100


def test1():
    global g_num
    g_num += 1
    print(".....in test1 g_num=%d...." % g_num)


def test2():
    print(".....in test2 g_num=%d...." % g_num)


def main():
    t1 = threading.Thread(target=test1)
    t2 = threading.Thread(target=test2)

    t1.start()
    time.sleep(1)

    t2.start()
    time.sleep(1)

    print(".....in main Thread g_num=%d...." % g_num)


if __name__ == "__main__":
    main()

运行结果

.....in test1 g_num=101....
.....in test2 g_num=101....
.....in main Thread g_num=101....

由此可见多线程共享全局变量


资源竞争:

数值越小资源竞争越准确。

数值越大资源竞争越激烈,误差越大。 例如

同步

同步就是协调步调,按预定的先后次序进行运行。如:你说完,我再说。

互斥锁

使用互斥锁解决资源竞争问题

import threading
import time


# 定义一个全局变量
g_num = 100


def test1(num):
    global g_num
    # 上锁,如果之前没有被上锁,那么此时上锁成功
    # 如果上锁之前已经被上锁,那么此时会堵塞在这里,直到这个锁被解开为止
    mutex.acquire()
    for i in range(num):
        g_num += 1
    mutex.release()
    print(".....in test1 g_num=%d...." % g_num)


def test2(num):
    global g_num
    mutex.acquire()
    for i in range(num):
        g_num += 1
    mutex.release()
    print(".....in test2 g_num=%d...." % g_num)


# 创建一个互斥锁,默认是没有上锁的
mutex = threading.Lock()


def main():
    t1 = threading.Thread(target=test1, args=(1000000,))
    t2 = threading.Thread(target=test2, args=(1000000,))

    t1.start()
    t2.start()

    # 等待上面的2个线程执行完毕...
    time.sleep(2)
    print(".....in main Thread g_num=%d...." % g_num)


if __name__ == "__main__":
    main()

多线程版udp聊天器

import socket
import threading


def recv_msg(udp_socket):
    """接收数据并显示"""

    # 接收数据
    while True:
        recv_data = udp_socket.recvfrom(1024)
        print(recv_data)


def send_msg(udp_socket, dest_ip, dest_port):
    """发送数据"""
    # 发送数据
    while True:
        send_data = input("请输入要发送的数据:")
        udp_socket.sendto(send_data.encode("utf-8"))


def main():
    """完成udp聊天器的整体控制"""

    # 创建套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 绑定本地信息
    udp_socket.bind(("", 7890))

    # 获取对方的ip
    dest_ip = input("请输入对方的ip:")
    dest_port = int(input("请输入对方的port:"))

    # 创建2个线程,去执行相应的功能
    t_recv = threading.Thread(target=recv_msg,args=(udp_socket,))
    t_send = threading.Thread(target=send_msg,args=(udp_socket, dest_ip, dest_port))

    t_recv.start()
    t_send.start()


if __name__ == "__main__":
    main()

进程

一个程序运行起来,代码——用到的资源称之为进程,它是操作系统分配资源的基本单元,因此导致了有了不同的状态

线程不能够独立执行,必须依存在进程中

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护:而进程正相反。

线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。

import time
import multiprocessing


def test1():
    while True:
        print("1.......")
        time.sleep(1)


def test2():
    while True:
        print("2.......")
        time.sleep(1)


def main():
    p1 = multiprocessing.Process(target=test1)
    p2 = multiprocessing.Process(target=test2)
    p1.start()
    p2.start()


if __name__ == "__main__":
    main()

通过队列完成进程间通信
import multiprocessing


def download_from_web(q):
    """下载数据"""
    # 模拟从网上下载的数据
    data = [11, 22, 33, 44]

    # 向队列中写入数据
    for temp in data:
        q.put(temp)

    print("...下载器已经下载完了数据并且存入到队列中...")


def analysis_data(q):
    """数据处理"""
    waiting_analysis_data = list()
    # 从队列中获取数据
    while True:
        data = q.get()
        waiting_analysis_data.append(data)

        if q.empty():
            break

    # 模拟数据处理
    print(waiting_analysis_data)


def main():
    # 创建一个队列
    q = multiprocessing.Queue()

    # 创建多个线程,将队列的引用当做实参进行传递到里面
    p1 = multiprocessing.Process(target=download_from_web, args=(q,))
    p2 = multiprocessing.Process(target=analysis_data, args=(q,))
    p1.start()
    p2.start()


if __name__ == "__main__":
    main()

进程池Pool

当需要创建的子进程数量不多时,可以直接利用mltiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的创建进程的工作量巨大,此时就可以用到multiprocessing横块提供的Pool方法。

初始化Pool时,可以指定-个最大进程数,当有新的请求提交到Pool中时如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果地中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务

from multiprocessing import Pool
import os, time, random


def worker(msg):
    t_start = time.time()
    print("%s开始执行,进程号为%d" % (msg, os.getpid()))
    # random.random()随机生成0-1之间的浮点数
    time.sleep(random.random()*2)
    t_stop = time.time()
    print(msg, "执行完毕,耗时%0.2f" % (t_stop-t_start))


po = Pool(3)  # 定义一个进程池,最大进程数3
for i in range(0, 10):
    # Pool().apply_async(要调用的目标,(传递给目标的参数元祖,))
    # 每次循环将会用空闲出来的子进程去调用目标
    po.apply_async(worker, (i,))

print("...start...")
po.close()  # 关闭        进程池,关闭后po不在就收新的请求
po.join()  # 等待po中所有子进程执行完成,必须放在close语句之后
print("...end...")

多任务文件夹copy
import os
import multiprocessing


def copy_file(q, file_name, old_folder_name, new_folder_name):
    """完成文件的复制"""
    print("====>模拟copy的文件夹的名字:从%s...>到%s 文件名是:%s" % (old_folder_name, new_folder_name, file_name))
    old_f = open(old_folder_name + "/" + file_name, "rb")
    content = old_f.read()
    old_f.close()

    new_f = open(new_folder_name + "/" + file_name, "wb")
    new_f.write(content)
    new_f.close()

    # 如果拷贝完了文件,那么就向队列中写入一个消息,表示已经完成
    q.put(file_name)


def main():
    # 1.获取用户要copy的文件夹名字
    old_folder_name = input("请输入要copy的文件夹的名字:")

    # 2.创建一个新的文件夹
    try:
        new_folder_name = old_folder_name + "[复件]"
        os.mkdir(new_folder_name)
    except:
        pass

    # 3.获取文件夹的所有的待copy的文件名字 listdir()
    file_names = os.listdir(old_folder_name)
    print(file_names)

    # 4.创建进程池
    po = multiprocessing.Pool(5)

    # 5.创建一个队列
    q = multiprocessing.Manager().Queue()

    # 6.向进程池中添加copy文件的任务
    for file_name in file_names:
        po.apply_async(copy_file, args=(q, file_name, old_folder_name, new_folder_name))

    po.close()
    # po.join()
    all_file_num = len(file_names)  # 测一下所有文件的个数
    copy_ok_num = 0
    while True:
        file_name = q.get()
        # print("已经完成copy:%s" % file_name)
        copy_ok_num += 1
        print("\r拷贝的进度为:%.2f %%" % (copy_ok_num*100/all_file_num), end="")
        if copy_ok_num >= all_file_num:
            break


if __name__ == '__main__':
    main()

协程

迭代器

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

import time
# from collections import Iterable
# from collections import Iterator


class Classmate(object):
    def __init__(self):
        self.names = list()
        self.current_num = 0

    def __add__(self, name):
        self.names.append(name)

    def __iter__(self):
        """如果想要一个对象称为一个可以迭代的对象,即可以使用for,那么必须实现__iter__方法"""
        return self

    def __next__(self):
        if self.current_num < len(self.names):
            ret = self.names[self.current_num]
            self.current_num += 1
            return ret
        else:
            raise StopIteration  # 如果产生了StopIteration异常,for循环自动停止


classmate = Classmate()
classmate.__add__("老王")
classmate.__add__("王二")
classmate.__add__("张三")

for name in classmate:
    print(name)
    time.sleep(1)

迭代器的应用

迭代器实现斐波那契数列

# import time
# from collections import Iterable
# from collections import Iterator


class Fibonacci(object):
    def __init__(self, all_num):
        self.all_num = all_num
        self.current_num = 0
        self.a = 0
        self.b = 1

    def __iter__(self):
        """如果想要一个对象称为一个可以迭代的对象,即可以使用for,那么必须实现__iter__方法"""
        return self

    def __next__(self):
        if self.current_num < self.all_num:
            ret = self.a
            self.a, self.b = self.b, self.a+self.b
            self.current_num += 1
            return ret
        else:
            raise StopIteration  # 如果产生了StopIteration异常,for循环自动停止


fibo = Fibonacci(10)


for num in fibo:
    print(num)

生成器

生成器是一种特殊的迭代器。

生成器是一次生成一个值的特殊类型函数。可以将其视为可恢复函数。调用该函数将返回一个可用于生成连续 x 值的生成【Generator】,简单的说就是在函数的执行过程中,yield语句会把你需要的值返回给调用生成器的地方,然后退出函数,下一次调用生成器函数的时候又从上次中断的地方开始执行,而生成器内的所有变量参数都会被保存下来供下一次使用。

我们创建了一个generator后,基本上永远不会调用next()方法,而是通过for循环来迭代它

不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器
第一种(不太常用)

num = [x * x for x in range(10)]

第二种

def create_num(all_num):
    # a = 0
    # b = 1
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        # print(a)
        # 如果一个函数中有yield语句,那么这个就不在是函数,而是一个生成器的模板
        yield a
        a, b = b, a+b
        current_num += 1
    return "ok..."


# 如果在调用create_num的时候,发现这个函数中有yield那么此时,不是调用函数,而是创建一个生成器对象
obj = create_num(10)

while True:
    try:
        ret = next(obj)
        print(ret)
    except Exception as ret:
        print(ret.value)
        break
使用yield完成多任务
import time


def task_1():
    while True:
        print("...1...")
        time.sleep(0.1)
        yield


def task_2():
    while True:
        print("...2...")
        time.sleep(0.1)
        yield


def main():
    t1 = task_1()
    t2 = task_2()
    while True:
        next(t1)
        next(t2)


if __name__ == "__main__":
    main()

使用greenlet完成多任务
from greenlet import greenlet
import time


def test1():
    while True:
        print("...A...")
        gr2.switch()
        time.sleep(0.5)


def test2():
    while True:
        print("...B...")
        gr1.switch()
        time.sleep(0.5)


gr1 = greenlet(test1)
gr2 = greenlet(test2)


# 切换到gr1运行
gr1.switch()

使用gevent完成多任务(重点)
import gevent
import time
from gevent import monkey


monkey.patch_all()


def f1(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
        # gevent.sleep(0.5)


def f2(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
        # gevent.sleep(0.5)


def f3(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
        # gevent.sleep(0.5)


gevent.joinall([
    gevent.spawn(f1, 5),
    gevent.spawn(f1, 5),
    gevent.spawn(f1, 5)
])

在使用延时切换其他任务时,必须使用gevent中的延时,但是在程序前使用monkey。patch_all()打补丁,可以自动使所有的延时操作变为gevent中的延时。

使用gevent.joinall([ ])这个列表可以直接将所创建的对象join,更为简洁


简单总结
  • 进程是资源分配的单位
  • 进程是操作系统调度的单位
  • 进程切换需要的资源最大,效率很低
  • 线程切换需要的资源一般,效率一般(当然在不考虑gil的情况下)
  • 协程切换任务资源很小,效率高
  • 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中,所以是并发
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值