首页 > 系统相关 >多线程与多进程

多线程与多进程

时间:2023-06-09 16:46:47浏览次数:52  
标签:__ main name print 线程 进程 多线程 def

多线程与多进程

一, 什么是进程, 什么是线程?

​ 进程: 运行中的程序. 每次我们执行一个程序, 咱们的操作系统对自动的为这个程序准备一些必要的资源(例如, 分配内存, 创建一个能够执行的线程. )

​ 线程: 程序内, 可以直接被CPU调度的执行过程. 是操作系统能够进行运算调度的最小单位. 它被包含在进程之中, 是进程中的实际运作单位.

​ 进程与线程之间的关系:

​ 进程是资源单位. 线程是执行单位. 就好比是一家公司. 一家公司的资源就是桌椅板凳, 电脑饮水机这些资源, 但是, 我们如果说一家公司正在运转着, 运行着. 那里面必须要有能为这家公司工作的人. 程序里面也一样, 进程就是为了程序运行而需要的各种资源. 但是程序想要运行, 就必须由线程来被CPU调度执行.

​ 我们运行的每一个程序默认都会有一个线程. 哪怕是只有helloworld级别的程序. 想要执行. 也会有一个线程产生.

二, 多线程

​ 顾名思义, 多线程就是让程序产生多个线程一起去执行. 还拿公司举例子. 一家公司里如果只有一个员工, 工作效率肯定不会高到哪里去. 怎么提高效率? 多招点儿人就OK了.

​ 如何实现多线程, 在python中, 有两种方案实现多线程.

1. 直接用Thread创建线程

我们先看看单线程的效果

def func():
    for i in range(1000):
        print("func", i)


if __name__ == '__main__':
    func()
    for i in range(1000):
        print("main", i)

再看多线程

from threading import Thread  # 线程

# 1. 定义好. 线程要做哪些任务
def func():
    for i in range(1000):
        print("子线程", i)


# 2. 写main, 创建子线程
if __name__ == '__main__':  # 要写这个
    # t = Thread(target=func)  # 创建一个子线程, 该线程还没有被执行
    # # 启动一个线程
    # t.start()
    # # 主线程继续执行下去.
    # for i in range(1000):
    #     print("主线程", i)
    t1 = Thread(target=func)
    t2 = Thread(target=func)
    t1.start()
    t2.start()

2. 含参数的线程

def func(url):
    # 编写爬虫的工作
    print("我要编写爬虫的工作", url)

if __name__ == '__main__':
    urls = ["第一个", "第二个", "第三个"]
    for u in urls:
        # 注意, 线程不是创建的越多就越好. CPU核心数 * 4
        t = Thread(target=func, args=(u, ))  # args可以给线程传递参数. 但是必须是元组.
        t.start()

3. 继承Thread类

class MyThread(Thread):  # 自己定义一个类. 继承Thread

    def __init__(self, name):
        super(MyThread, self).__init__()
        self.name = name

    def run(self):  # 固定的.  # 必须要编写run方法
        for i in range(1000):
            print(self.name, i)


if __name__ == '__main__':
    t1 = MyThread("线程1")
    t2 = MyThread("线程2")

    t1.start()
    t2.start()

以上两种是最基本的python创建多线程的方案. python还提供了线程池

4. 线程池

python还提供了线程池功能. 可以一次性的创建多个线程, 并且, 不需要我们程序员手动去维护. 一切都交给线程池来自动管理.

# 线程池
def fn(name):
    for i in range(1000):
        print(name, i)


if __name__ == '__main__':
    with ThreadPoolExecutor(10) as t:
        for i in range(100):
            t.submit(fn, name=f"线程{i}")

如果任务有返回值怎么办?


def func(name):
    time.sleep(2)
    return name


def do_callback(res):
    print(res.result())


if __name__ == '__main__':
    with ThreadPoolExecutor(10) as t:
        names = ["线程1", "线程2", "线程3"]
        for name in names:
            # 方案一, 添加回调
            t.submit(func, name).add_done_callback(do_callback)

            
if __name__ == '__main__':
    start = time.time()
    with ThreadPoolExecutor(10) as t:
        names = [5, 2, 3]
        # 方案二, 直接用map进行任务分发. 最后统一返回结果
        results = t.map(func, names, )  # 结果是按照你传递的顺序来执行的, 代价就是如果第一个没结束. 后面就都没结果
        for r in results:
            print("result", r)
    print(time.time() - start)

5. 多线程在爬虫中的应用

http://www.xinfadi.com.cn/marketanalysis/0/list/1.shtml

依然用新发地这个案例.

import requests
from lxml import etree
from concurrent.futures import ThreadPoolExecutor


def get_page_source(url):
    resp = requests.get(url)
    return resp.text


def get_totle_count():
    url = "http://www.xinfadi.com.cn/marketanalysis/0/list/1.shtml"
    source = get_page_source(url)
    tree = etree.HTML(source)
    last_href = tree.xpath("//div[@class='manu']/a[last()]/@href")[0]
    totle = last_href.split("/")[-1].split(".")[0]
    return int(totle)


def download_content(url):
    source = get_page_source(url)
    tree = etree.HTML(source)
    trs = tree.xpath("//table[@class='hq_table']/tr[position() > 1]")
    result = []
    for tr in trs:
        tds = tr.xpath("./td/text()")
        result.append((tds[0], tds[1], tds[2], tds[3], tds[4], tds[5], tds[6]))
    return result


def main():
    f = open("data.csv", mode="w")
    totle = get_totle_count()
    url_tpl = "http://www.xinfadi.com.cn/marketanalysis/0/list/{}.shtml"

    with ThreadPoolExecutor(50) as t:
        data = t.map(download_content, (url_tpl.format(i) for i in range(1, totle+1)))
        # 拿到所有任务的返回
        for item in data:
            # 每个任务的数据循环出一行
            for detial in item:
                # 写入文件
                content = ",".join(detial) + "\n"
                print(content)
                f.write(content)


if __name__ == '__main__':
    main()

三, 多进程

一个公司能创造的价值毕竟是有限的. 怎么办? 开分公司啊. 此所谓多进程. python实现多进程的方案和多线程几乎一样. 非常的简单

1. 直接用Process创建进程

def func():
    for i in range(1000):
        print("func", i)


if __name__ == '__main__':
    p = Process(target=func)
    p.start()

    for i in range(1000):
        print("main", i)

2. 继承Process类

class MyProcess(Process):
    def run(self):
        for i in range(1000):
            print("MyProcess", i)


if __name__ == '__main__':
    t = MyProcess()
    t.start()
    for i in range(1000):
        print("main", i)

3.多进程在爬虫中的应用

​ 我们一般很少直接使用多进程. 最适合使用多进程的情况是: 多个任务需要一起执行. 并且互相之间数据可能有交汇但功能相对独立.比如, 我们自己做一个代理IP池, 就需要从网络上进行抓取, 抓取得到的IP要进行校验才可以进行使用. 此时, 抓取任务和校验任务就相当于完全独立的两个功能. 此时就可以启动多个进程来实现. 再比如, 如果遇到图片抓取的时候, 我们知道图片在一般都在网页的img标签中src属性存放的是图片的下载地址. 此时我们可以采用多进程的方案来实现, 一个负责疯狂扫图片下载地址. 另一个进程只负责下载图片.

​ 综上, 多个任务需要并行执行, 但是任务之间相对独立(不一定完全独立). 可以考虑用多进程.

# 进程1. 从图片网站中提取到图片的下载路径
def get_pic_src(q):
    print("start main page spider")
    url = "http://www.591mm.com/mntt/"
    resp = requests.get(url)
    tree = etree.HTML(resp.text)
    child_hrefs = tree.xpath("//div[@class='MeinvTuPianBox']/ul/li/a/@href")
    print("get hrefs from main page", child_hrefs)
    for href in child_hrefs:
        href = parse.urljoin(url, href)
        print("handle href", href)
        resp_child = requests.get(href)
        tree = etree.HTML(resp_child.text)
        pic_src = tree.xpath("//div[@id='picBody']//img/@src")[0]
        print(f"put {pic_src} to the queue")
        q.put(pic_src)
        # 作业, 分页图片抓取
        # print("ready to another!")
        # others = tree.xpath('//ul[@class="articleV2Page"]')
        # if others:


# 进程2. 从图片网站中提取到图片的下载路径
def download(url):
    print("start download", url)
    name = url.split("/")[-1]
    resp = requests.get(url)
    with open(name, mode="wb") as f:
        f.write(resp.content)
    resp.close()
    print("downloaded", url)


def start_download(q):
    with ThreadPoolExecutor(20) as t:
        while True:
            t.submit(download, q.get())  # 启动

            
def main():
    q = Queue()
    p1 = Process(target=start_download, args=(q,))
    p2 = Process(target=get_pic_src, args=(q,))
    p1.start()
    p2.start()


if __name__ == '__main__':
    main()

标签:__,main,name,print,线程,进程,多线程,def
From: https://www.cnblogs.com/stonejc/p/17469608.html

相关文章

  • Python进阶:进程的状态及基本操作
    文章目录Python进阶篇-系列文章全篇一、进程以及状态二、[重点]进程-基本使用三、[重点]进程-名称、PID四、[重点]进程-参数传递、全局变量问题五、[重点]进程-守护主进程六、进程、线程对比七、[重点]消息队列-基本操作八、消息队列-常见判断九、[重点]Queue实现进程间通信十、[......
  • 关于Pod中进程在节点中的研究
    最近研究OpenShiftvirtulization,各种Pod对KVM进程的封装,引发了Pod中进程到底在Node中是什么表现形势的好奇,因为对基础知识的不扎实,还是希望找个环境能仔细看看,建立起openshift4.12的环境后,首先列出某个节点上的所有的Pod[lab-user@bastion~]$ocgetpods-A--field-selec......
  • Nginx大文件分片上传/多线程上传
    ​ 前言一、SpringMVC简介1.1、SpringMVC引言为了使Spring有可插入的MVC架构,SpringFrameWork在Spring基础上开发SpringMVC框架,从而在使用Spring进行WEB开发时可以选择使用Spring的SpringMVC框架作为web开发的控制器框架。 spring知识图谱分享:1.2、SpringMV......
  • wsexplorer——windows下的抓包工具 可以直接抓进程对应的网络流量
    软件标签:WSExplorer抓包工具  wsexplorer1.5版本是一款非常实用的抓包工具,用户能够直接通过软件直接获取更多的数据,同时还设计了选择功能,只需挑选自己需要的数据,需要的用户快来绿色资源网下载吧!wsexplorer抓包工具简介:wsexplorer是最好用的抓包工具,1.5版本添加新功能,分离二进......
  • 进程的调度
    进程的调度算法的本质是为了优化为了让进程更好地被OS调度,因此我们需要理解什么是调度,而不是一开始就明白调度算法有什么?1.调度个人觉得调度虽然是一种计算机机制,实际上也是一种思想:比如:在资源有限的情况下,需要分配一定的额度,这时候你就需要考虑去如何分配,也就是如何......
  • 1.进程的创建
    进程概念程序存放在磁盘上的指令和数据的有序集合(文件)静态的 进程执行一个程序所分配的资源的总称动态的 进程和程序内容区别   进程包含的内容:BSS段:存放程序中未初始化的全局变量数据段:已初始化的全局变量代码段:程序执行代码堆(heap):malloc等函数分配内存栈......
  • windows查看java进程, 终止进程命令
    查看:tasklist| findstr "java"终止:taskkill/pid20388/f/f表示强制终止......
  • 多线程中的上下文切换
    我们都知道,在并发编程中,并不是线程越多就效率越高,线程数太少可能导致资源不能充分利用,线程数太多可能导致竞争资源激烈,然后上下文切换频繁造成系统的额外开销。大量的超时报警,通过工具分析,cs指标很高,然后分析日志,发现有大量wait()相关的Exception,这个时候我们怀疑是在多线程并发处......
  • 关于Java中多线程
    基本概念什么是进程-->是操作系统资源分配和调度的最小(基本)单位(操作系统分配给当前进程一个内存区域供其使用)什么是线程-->是程序运行的基本单位(等待操作系统分配时间片让CPU执行该内存区域中的代码)进程和线程的关系-->一个进程可以存在多个线程线程是由进程创建的(寄......
  • Linux分析进程占用内存最高和占用CPU最高 的 命令
    Linux分析进程占用内存最高和占用CPU最高这里只显示最高的前5个,如果想显示更多的话,可以自己修改:查看占用内存最高的5个进程psaux|sort-k4nr|head-n5查看占用cpu最高的5个进程psaux|sort-k3nr|head-n5......