精华内容
下载资源
问答
  • python3.6threading和multiprocessing四核+三星250G-850-SSD自从用多进程和多线程进行编程,一致没搞懂到底谁更。网上很多都说python多进程更,因为GIL(全局解释器锁)。但是我在写代码的时候,测试时间却是...

    python3.6

    threading和multiprocessing

    四核+三星250G-850-SSD

    自从用多进程和多线程进行编程,一致没搞懂到底谁更快。网上很多都说python多进程更快,因为GIL(全局解释器锁)。但是我在写代码的时候,测试时间却是多线程更快,所以这到底是如何回事?最近再做分词工作,原来的代码速度太慢,想提速,所以来探求一下有效方法(文末有代码和效果图)

    这里先来一张程序的结果图,介绍线程和进程谁更快

    一些定义

    并行是指两个或者多个事件在同一时刻发生。并发是指两个或多个事件在同一时间间隔内发生

    线程是操作系统能够进行运算调度的最小单位。它被共含在进程之中,是进程中的实际运作单位。一个程序的执行实例就是一个进程。

    实现过程

    而python里面的多线程显然得拿到GIL,执行code,最后释放GIL。所以由于GIL,多线程的时候拿不到,实际上,它是并发实现,即多个事件,在同一时间间隔内发生。

    但进程有独立GIL,所以可以并行实现。因此,针对多核CPU,理论上使用多进程更能有效利用资源。

    现实问题

    在网上的教程里面,经常能见到python多线程的身影。比如网络爬虫的教程、端口扫描的教程。

    这里拿端口扫描来说,大家可以用多进程实现下面的脚本,会发现python多进程更快。那么不就是和我们分析相悖了吗?

    import sys,threading

    from socket import *

    host = "127.0.0.1" if len(sys.argv)==1 else sys.argv[1]

    portList = [i for i in range(1,1000)]

    scanList = []

    lock = threading.Lock()

    print('Please waiting... From ',host)

    def scanPort(port):

    try:

    tcp = socket(AF_INET,SOCK_STREAM)

    tcp.connect((host,port))

    except:

    pass

    else:

    if lock.acquire():

    print('[+]port',port,'open')

    lock.release()

    finally:

    tcp.close()

    for p in portList:

    t = threading.Thread(target=scanPort,args=(p,))

    scanList.append(t)

    for i in range(len(portList)):

    scanList[i].start()

    for i in range(len(portList)):

    scanList[i].join()

    谁更快

    因为python锁的问题,线程进行锁竞争、切换线程,会消耗资源。所以,大胆猜测一下:

    在CPU密集型任务下,多进程更快,或者说效果更好;而IO密集型,多线程能有效提高效率。

    大家看一下下面的代码:

    import time

    import threading

    import multiprocessing

    max_process = 4

    max_thread = max_process

    def fun(n,n2):

    #cpu密集型

    for i in range(0,n):

    for j in range(0,(int)(n*n*n*n2)):

    t = i*j

    def thread_main(n2):

    thread_list = []

    for i in range(0,max_thread):

    t = threading.Thread(target=fun,args=(50,n2))

    thread_list.append(t)

    start = time.time()

    print(' [+] much thread start')

    for i in thread_list:

    i.start()

    for i in thread_list:

    i.join()

    print(' [-] much thread use ',time.time()-start,'s')

    def process_main(n2):

    p = multiprocessing.Pool(max_process)

    for i in range(0,max_process):

    p.apply_async(func = fun,args=(50,n2))

    start = time.time()

    print(' [+] much process start')

    p.close()#关闭进程池

    p.join()#等待所有子进程完毕

    print(' [-] much process use ',time.time()-start,'s')

    if __name__=='__main__':

    print("[++]When n=50,n2=0.1:")

    thread_main(0.1)

    process_main(0.1)

    print("[++]When n=50,n2=1:")

    thread_main(1)

    process_main(1)

    print("[++]When n=50,n2=10:")

    thread_main(10)

    process_main(10)

    结果如下:

    可以看出来,当对cpu使用率越来越高的时候(代码循环越多的时候),差距越来越大。验证我们猜想

    CPU和IO密集型

    1、CPU密集型代码(种种循环处理、计数等等)

    2、IO密集型代码(文件处理、网络爬虫等)

    判断方法:

    1、直接看CPU占用率, 硬盘IO读写速度

    2、计算较多->CPU;时间等待较多(如网络爬虫)->IO

    3、请自己百度

    以上这篇python多进程和多线程究竟谁更快(详解)就是小编共享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持乐购源码。

    展开全文
  • 实现爬虫的高并发不仅仅只有协程异步这一种手段,传统的同步爬虫结合多进程和多线程也能大大提升爬虫工作效率,那么多进程, 多线程和异步协程爬虫到底谁更呢? 当然对于现实中的爬虫,爬得越,被封的可能性也越高...

    Python爬虫下一代网络请求库httpx和parsel解析库测评一文中我们对比了requests的同步爬虫和httpx的异步协程爬虫爬取链家二手房信息所花的时间(如下所示:一共580条记录),结果httpx同步爬虫花了16.1秒,而httpx异步爬虫仅花了2.5秒。

    那么问题来了。实现爬虫的高并发不仅仅只有协程异步这一种手段,传统的同步爬虫结合多进程和多线程也能大大提升爬虫工作效率,那么多进程, 多线程和异步协程爬虫到底谁更快呢? 当然对于现实中的爬虫,爬得越快,被封的可能性也越高。本次测评使用httpx爬取同样链家网数据,不考虑反爬因素,测评结果可能因个人电脑和爬取网站对象而异。

    在我们正式开始前,你能预测下哪种爬虫更快吗?可能结果会颠覆你的观点。

    传统爬虫 vs 协程异步爬虫

    传统Python爬虫程序都是运行在单进程和单线程上的,包括httpx异步协程爬虫。如果你不清楚进程和线程的区别,以及Python如何实现多进程和多线程编程,请阅读下面这篇知乎上收藏过1000的文章。

    一个传统的web爬虫代码可能如下所示,先用爬虫获取目标页面中显示的最大页数,然后循环爬取每个单页数据并解析。单进程、单线程同步爬虫的请求是阻塞的,在一个请求处理完全结束前不会发送一个新的请求,中间浪费了很多等待时间。

    httpx异步协程爬虫虽然也是运行在单进程单线程上的,但是所有异步任务都会加到事件循环(loop)中运行,可以一次有上百或上千个活跃的任务,一旦某个任务需要等待,loop会快速切换到下面一个任务,所以协程异步要快很多。

    要把上面的同步爬虫变为异步协程爬虫,我们首先要使用async将单个页面的爬取和解析过程包装成异步任务,使用httpx提供的AsyncClient发送异步请求。

    接着我们使用asyncio在主函数parse_page里获取事件循环(loop), 并将爬取单个页面的异步任务清单加入loop并运行。

    多进程爬虫

    对于多线程爬虫,我们首先定义一个爬取并解析单个页面的同步任务。

    接下来我们在主函数parse_page里用multiprocessing库提供的进程池Pool来管理多进程任务。池子里进程的数量,一般建议为CPU的核数,这是因为一个进程需要一个核,你设多了也没用。我们使用map方法创建了多进程任务,你还可以使用apply_async方法添加多进程任务。任务创建好后,任务的开始和结束都由进程池来管理,你不需要进行任何操作。这样我们一次就有4个进程同时在运行了,一次可以同时处理4个请求。

    那用这个多进程爬虫爬取链家580条数据花了多长时间呢? 答案是7.6秒,比单进程单线程的httpx同步爬虫16.1秒还是要快不少的。

    项目完整代码如下所示:

    from fake_useragent import UserAgent
    import csv
    import re
    import time
    from parsel import Selector
    import httpx
    from multiprocessing import Pool, cpu_count, Queue, Manager
    
    
    class HomeLinkSpider(object):
        def __init__(self):
            # 因为多进程之间不能共享内存,需使用队列Queue共享数据进行通信
            # 每个进程爬取的数据都存入这个队列,不能使用self.data列表
            # 子进程获取不到self.headers这个变量,需要直接生成
            # self.ua = UserAgent()
            # self.headers = {"User-Agent": self.ua.random}
            self.q = Manager().Queue()
            self.path = "浦东_三房_500_800万.csv"
            self.url = "https://sh.lianjia.com/ershoufang/pudong/a3p5/"
        def get_max_page(self):
            response = httpx.get(self.url, headers={"User-Agent": UserAgent().random})
            if response.status_code == 200:
                # 创建Selector类实例
                selector = Selector(response.text)
                # 采用css选择器获取最大页码div Boxl
                a = selector.css('div[class="page-box house-lst-page-box"]')
                # 使用eval将page-data的json字符串转化为字典格式
                max_page = eval(a[0].xpath('//@page-data').get())["totalPage"]
                print("最大页码数:{}".format(max_page))
                return max_page
            else:
                print("请求失败 status:{}".format(response.status_code))
                return None
        # 解析单页面,需传入单页面url地址
        def parse_single_page(self, url):
            print("子进程开始爬取:{}".format(url))
            response = httpx.get(url, headers={"User-Agent": UserAgent().random})
            selector = Selector(response.text)
            ul = selector.css('ul.sellListContent')[0]
            li_list = ul.css('li')
            for li in li_list:
                detail = dict()
                detail['title'] = li.css('div.title a::text').get()
    
    
                #  2室1厅 | 74.14平米 | 南 | 精装 | 高楼层(共6层) | 1999年建 | 板楼
                house_info = li.css('div.houseInfo::text').get()
                house_info_list = house_info.split(" | ")
    
    
                detail['bedroom'] = house_info_list[0]
                detail['area'] = house_info_list[1]
                detail['direction'] = house_info_list[2]
    
    
    
    
                floor_pattern = re.compile(r'\d{1,2}')
                match1 = re.search(floor_pattern, house_info_list[4])  # 从字符串任意位置匹配
                if match1:
                    detail['floor'] = match1.group()
                else:
                    detail['floor'] = "未知"
                # 匹配年份
                year_pattern = re.compile(r'\d{4}')
                match2 = re.search(year_pattern, house_info_list[5])
                if match2:
                    detail['year'] = match2.group()
                else:
                    detail['year'] = "未知"
                # 文兰小区 - 塘桥    提取小区名和哈快
                position_info = li.css('div.positionInfo a::text').getall()
                detail['house'] = position_info[0]
                detail['location'] = position_info[1]
    
    
                # 650万,匹配650
                price_pattern = re.compile(r'\d+')
                total_price = li.css('div.totalPrice span::text').get()
                detail['total_price'] = re.search(price_pattern, total_price).group()
    
    
                # 单价64182元/平米, 匹配64182
                unit_price = li.css('div.unitPrice span::text').get()
                detail['unit_price'] = re.search(price_pattern, unit_price).group()
    
    
                self.q.put(detail)
    
    
        def parse_page(self):
            max_page = self.get_max_page()
            print("CPU内核数:{}".format(cpu_count()))
    
    
            # 使用进程池管理多进程任务
            with Pool(processes=4) as pool:
                urls = ['https://sh.lianjia.com/ershoufang/pudong/pg{}a3p5/'.format(i) for i in range(1, max_page + 1)]
                # 也可以使用pool.apply_async(self.parse_single_page, args=(url,))
                pool.map(self.parse_single_page, urls)
    
    
    
    
        def write_csv_file(self):
            head = ["标题", "小区", "房厅", "面积", "朝向", "楼层", "年份", "位置", "总价(万)", "单价(元/平方米)"]
            keys = ["title", "house", "bedroom", "area", "direction", "floor", "year", "location",
                    "total_price", "unit_price"]
    
    
            try:
                with open(self.path, 'w', newline='', encoding='utf_8_sig') as csv_file:
                    writer = csv.writer(csv_file, dialect='excel')
                    if head is not None:
                        writer.writerow(head)
    
    
                    # 如果队列不为空,写入每行数据
                    while not self.q.empty():
                        item = self.q.get()
                        if item:
                            row_data = []
                            for k in keys:
                                row_data.append(item[k])
                            writer.writerow(row_data)
    
    
                    print("Write a CSV file to path %s Successful." % self.path)
            except Exception as e:
                print("Fail to write CSV to path: %s, Case: %s" % (self.path, e))
    
    
    
    
    if __name__ == '__main__':
        start = time.time()
        home_link_spider = HomeLinkSpider()
        home_link_spider.parse_page()
        home_link_spider.write_csv_file()
        end = time.time()
        print("耗时:{}秒".format(end-start))
    

    注意: 多个进程之间内存是不共享的,需要使用Python多进程模块提供的Manager.Queue()实现多个进程的数据共享,比如把不同进程爬取的数据存到一个地方。

    多线程爬虫

    爬取解析单个页面的函数和多进程爬虫里的代码是一样的,不同的是我们在parse_page主函数里使用threading模块提供的方法创建多线程任务,如下所示:

    我们也不需要使用Queue()类存储各个线程爬取的数据,因为各个线程内存是可以共享的。多线程同步爬虫运行结果如下所示,爬取580条数据总共耗时只有短短的2.2秒,几乎秒开,甚至比httpx异步协程的还快!

    结果为什么是这样呢?其实也不难理解。对于爬虫这种任务,大部分消耗时间其实是等等待时间,在等待时间中CPU是不需要工作的,那你在此期间提供双核或4核CPU进行多进程编程是没有多大帮助的。那么为什么多线程会对爬虫代码有用呢?这时因为Python碰到等待会立即释放GIL供新的线程使用,实现了线程间的快速切换,这跟协程异步任务的切换一个道理,只不过多线程任务的切换由操作系统进行,而协程异步任务的切换由loop进行。

    多线程完整代码如下所示:

    from fake_useragent import UserAgent
    import csv
    import re
    import time
    from parsel import Selector
    import httpx
    import threading
    
    
    
    
    class HomeLinkSpider(object):
        def __init__(self):
            self.data = list()
            self.path = "浦东_三房_500_800万.csv"
            self.url = "https://sh.lianjia.com/ershoufang/pudong/a3p5/"
    
    
        def get_max_page(self):
            response = httpx.get(self.url, headers={"User-Agent": UserAgent().random})
            if response.status_code == 200:
                # 创建Selector类实例
                selector = Selector(response.text)
                # 采用css选择器获取最大页码div Boxl
                a = selector.css('div[class="page-box house-lst-page-box"]')
                # 使用eval将page-data的json字符串转化为字典格式
                max_page = eval(a[0].xpath('//@page-data').get())["totalPage"]
                print("最大页码数:{}".format(max_page))
                return max_page
            else:
                print("请求失败 status:{}".format(response.status_code))
                return None
    
    
        # 解析单页面,需传入单页面url地址
        def parse_single_page(self, url):
            print("多线程开始爬取:{}".format(url))
            response = httpx.get(url, headers={"User-Agent": UserAgent().random})
            selector = Selector(response.text)
            ul = selector.css('ul.sellListContent')[0]
            li_list = ul.css('li')
            for li in li_list:
                detail = dict()
                detail['title'] = li.css('div.title a::text').get()
    
    
                #  2室1厅 | 74.14平米 | 南 | 精装 | 高楼层(共6层) | 1999年建 | 板楼
                house_info = li.css('div.houseInfo::text').get()
                house_info_list = house_info.split(" | ")
    
    
                detail['bedroom'] = house_info_list[0]
                detail['area'] = house_info_list[1]
                detail['direction'] = house_info_list[2]
    
    
    
    
                floor_pattern = re.compile(r'\d{1,2}')
                match1 = re.search(floor_pattern, house_info_list[4])  # 从字符串任意位置匹配
                if match1:
                    detail['floor'] = match1.group()
                else:
                    detail['floor'] = "未知"
    
    
                # 匹配年份
                year_pattern = re.compile(r'\d{4}')
                match2 = re.search(year_pattern, house_info_list[5])
                if match2:
                    detail['year'] = match2.group()
                else:
                    detail['year'] = "未知"
    
    
                # 文兰小区 - 塘桥    提取小区名和哈快
                position_info = li.css('div.positionInfo a::text').getall()
                detail['house'] = position_info[0]
                detail['location'] = position_info[1]
    
    
                # 650万,匹配650
                price_pattern = re.compile(r'\d+')
                total_price = li.css('div.totalPrice span::text').get()
                detail['total_price'] = re.search(price_pattern, total_price).group()
    
    
                # 单价64182元/平米, 匹配64182
                unit_price = li.css('div.unitPrice span::text').get()
                detail['unit_price'] = re.search(price_pattern, unit_price).group()
    
    
                self.data.append(detail)
    
    
        def parse_page(self):
            max_page = self.get_max_page()
    
    
            thread_list = []
            for i in range(1, max_page + 1):
                url = 'https://sh.lianjia.com/ershoufang/pudong/pg{}a3p5/'.format(i)
                t = threading.Thread(target=self.parse_single_page, args=(url,))
                thread_list.append(t)
    
    
            for t in thread_list:
                t.start()
    
    
            for t in thread_list:
                t.join()
    
    
        def write_csv_file(self):
            head = ["标题", "小区", "房厅", "面积", "朝向", "楼层", "年份", "位置", "总价(万)", "单价(元/平方米)"]
            keys = ["title", "house", "bedroom", "area", "direction", "floor", "year", "location",
                    "total_price", "unit_price"]
    
    
            try:
                with open(self.path, 'w', newline='', encoding='utf_8_sig') as csv_file:
                    writer = csv.writer(csv_file, dialect='excel')
                    if head is not None:
                        writer.writerow(head)
                    for item in self.data:
                        row_data = []
                        for k in keys:
                            row_data.append(item[k])
                            # print(row_data)
                        writer.writerow(row_data)
                    print("Write a CSV file to path %s Successful." % self.path)
            except Exception as e:
                print("Fail to write CSV to path: %s, Case: %s" % (self.path, e))
    
    
    if __name__ == '__main__':
        start = time.time()
        home_link_spider = HomeLinkSpider()
        home_link_spider.parse_page()
        home_link_spider.write_csv_file()
        end = time.time()
        print("耗时:{}秒".format(end-start))
    

    结论

    多进程, 多线程和异步协程均可以提高Python爬虫的工作效率。对于爬虫这种非计算密集型的工作,多进程编程对效率的提升不如多线程和异步协程。异步爬虫不总是最快的,同步爬虫+多线程也同样可以很快,有时甚至更快。

    • httpx 同步 + parsel: 16.1秒

    • httpx 异步 + parsel: 2.5秒

    • http 同步多进程 + parsel: 7.6秒

    • http 同步多线程 + parsel: 2.2秒

    对于这样的结果,你满意吗? 欢迎留言!

    大江狗

    2021.5

    推荐阅读

    神文必读: 同步Python和异步Python的区别在哪里?

    Python爬虫下一代网络请求库httpx和parsel解析库测评

    一文看懂Python多进程与多线程编程(工作学习面试必读)

    非常适合小白的 Asyncio 教程

    展开全文
  • CPU多核多线程到底有什么用?

    千次阅读 2020-12-22 11:44:00
    你去电子城装电脑档口推销人员都会推销电脑时会给你说,我这电脑是多少多少核,多少多少线程大内存,大显存之类的。其中我们最关心的无非就CPU,内存,显卡显存,其中最关心的可能就是CPU了。为什么呢,因为...

    你去电子城装电脑档口推销人员都会推销电脑时会给你说,我这电脑是多少多少核,多少多少线程,多大内存,多大显存之类的。其中我们最关心的无非就CPU,内存,显卡显存,其中最关心的可能就是CPU了。为什么呢,因为其它的都是死的,只要不是假货,那些参数都非常容易选。CPU的性能就决定了你电脑的性能。

    不过其中的一些小坑小伙伴们得注意了。电脑发展太快,很多硬件追都追不上,不是整天接触这类东西的人都不知道现在市场硬件有哪些迭代。加上很多人对电脑并不懂,推销员看你不咋懂便会在介绍时忽悠你。比如经常会拿2核4线程的说成是4核。有人可能会问这有啥区别?双核就是CPU内置真正的2个核心,CPU运作时各自处理各自任务,互不干扰。线程又是什么,线程是指一个CPU分离出来的一个任务。本质就是一个核心通过CPU不断的切换同时进行的任务工作。因为CPU速度非常快,让你感觉不到有切换,但本身是只有一核心在工作。

    现在来说说多核多线程,CPU频率到底该怎么选购。各自又决定了哪些性能,其实如果不谈使用场景,那就是核越多越好,线程越多越好,CPU频率越高越好。如果不谈场景这样是没意义的,比如服务器用的CPU和我们用的CPU从性能上讲单个比自用的还差,他性能上某些方面并不高。为什么,因为服务器要的是稳定,CPU数,线程数,并发量等,需求是不一样的。

    回头说回自用多核多线程,多核数决定了你运作程序时最多能有多少程序独占一个核心工作互不干扰。多线程决定了CPU一个核心下同时处理多少任务互不干扰,当然带来的就是性能上的折扣。核数并非越多性能就一定越好,并不决定你运行单个程序的性能提升,这里还和程序上非常大的关系。。拿现在的3D大型游戏来说,也就最多4核工作,所以你弄个什么8核,16核对你玩游戏是不会有提升的。那该怎么选,这时就要看单核的外频高低了,这时单核外频的高低就决定了你玩游戏的性能,暂不谈显卡,显卡是死的,好的显卡就那些,看显存就可以了。

    如果你说我不是玩游戏,我是弄视频,图片,或3D建模的怎么选。现在这些程序都会有GPU加速,所以这类程序主要从显卡上选择,对CPU要求反而没那么高。

    如果你主要是用来处理多任务的,比如我需要非常多的程序同时运行,但这些程序对CPU外频要求又不高,这时就要考虑多核多线程的CPU了,CPU的外频高低可以放低一些。

    看完了你应该知道怎么选择了吧,总之不差钱,不考虑钱,就往核越多越好,频越高越好,线程越多越好撸之

    突然发现说了好像根没说一样呢?哈哈。。

    展开全文
  • 现代计算机一般都是多核cpu,多线程的可以大大提高效率,但是可能会疑问,那单核CPU使用多线程是不是没有必要了,假定一种情况,web应用服务器,单核CPU、单线程,用户发过来请求,单个线程处理,CPU等待这个线程...

    问题分析

    现代计算机一般都是多核cpu,多线程的可以大大提高效率,但是可能会有疑问,那单核CPU使用多线程是不是没有必要了,假定一种情况,web应用服务器,单核CPU、单线程,用户发过来请求,单个线程处理,CPU等待这个线程的处理结果返回,查询数据库,CPU等待查询结果...,只有一个线程的话,每次线程在处理的过程中CPU都有大量的空闲等待时间,那这样来说并行和串行似乎并没有体现并行的优势,因为任务的总量在那里,实际情况肯定不是这样的,即便是单核CPU,一个进程中往往也是有多个线程存在的,每个线程各司其职,CPU来调度各线程。

    这里需要区分CPU处理指令和IO读取的不同,CPU的执行速度要远大于IO的过程,因此在大多数情况下多一些复杂的CPU计算都比增加一次IO要快,这一块深入理解要学习计算机原理相关的知识。

    现实生活中也是有很多类似的例子,比如厨师做一道菜,买菜和买配料需要去不同的两个商店,如果这个过程只依靠他一个人来做,那耗费的总时间就是买菜再去买调料的总时间,如果有一个帮厨,那么就可以兵分两路,再来汇总结果,时间基本可以减半,厨师和帮厨就是不同的线程。

    编程是高度抽象生活的一门艺术。

    场景模拟

    模拟单线程和多线程的效率差距,这里使用连接数据库,和读取磁盘文件来模拟IO操作,期望结果:

    单线程总耗时:数据库连接耗时 + 磁盘文件读取耗时
    多线程总耗时:约等于耗时最长的那个时间

    读取文件:https://gitee.com/chsoul/javase/blob/master/medias/testIO.txt

    MySQL8.0 连接驱动:https://gitee.com/chsoul/javase/blob/master/medias/mysql-connector-java-8.0.18.jar

    代码如下:

    package com.thread.demo;
    
    import java.io.*;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    /**
     * @author Vicente
     * @version 1.0
     * @date 2020/4/6 21:53
     */
    public class Test {
        /**
         * 数据库连接
         */
        public static void getMysqlData(){
            long t1 = System.currentTimeMillis();
            String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true";
            String userName = "root";
            String password = "root";
            try {
                Class.forName("com.mysql.cj.jdbc.Driver");
                Connection connection = DriverManager.getConnection(url, userName, password);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            long t2 = System.currentTimeMillis();
            System.out.println("数据库连接完成!耗时:"+(t2 - t1));
        }
    
        /**
         * 磁盘读取
         */
        public static void getDiskData(){
            long t1 = System.currentTimeMillis();
            File file = new File("src/com/thread/demo/test.txt");
            StringBuilder stb = new StringBuilder();
            InputStream is = null;
            try {
                is = new FileInputStream(file);
                int read = 0;
                while ((read = is.read()) != -1) {
                    char c = (char) read;
                    stb.append(c);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            long t2 = System.currentTimeMillis();
            System.out.println("磁盘文件读取结束!耗时:"+(t2 - t1));
        }
    
        public static void main(String[] args){
            System.out.println("-----------------单线程执行任务开始-----------------");
            long start = System.currentTimeMillis();
            getMysqlData();
            getDiskData();
            long end = System.currentTimeMillis();
            System.out.println("总耗时:"+(end - start));
            System.out.println("-----------------单线程执行任务结束-----------------");
            System.out.println("\r\n");
            try {
                System.out.println("-----------------多线程执行任务开始-----------------");
                long start1 = System.currentTimeMillis();
                FutureTask dbWork = new FutureTask(new DbDataWork());
                FutureTask diskWork = new FutureTask(new DiskDataWork());
                new Thread(dbWork).start();
                new Thread(diskWork).start();
                while (diskWork.get().equals("DISK_OK") && dbWork.get().equals("DB_OK")){
                    long end1 = System.currentTimeMillis();
                    System.out.println("总耗时:"+(end1 - start1));
                    System.out.println("-----------------多线程执行任务结束-----------------");
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 数据库连接任务类
     */
    class DbDataWork implements Callable<String> {
        @Override
        public String call() throws Exception {
            long t1 = System.currentTimeMillis();
            String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true";
            String userName = "root";
            String password = "root";
            try {
                Class.forName("com.mysql.cj.jdbc.Driver");
                Connection connection = DriverManager.getConnection(url, userName, password);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            long t2 = System.currentTimeMillis();
            System.out.println("数据库连接线程任务结束!耗时:"+(t2 - t1));
            return "DB_OK";
        }
    }
    
    /**
     * 磁盘读取任务类
     */
    class DiskDataWork implements Callable<String>{
        @Override
        public String call() throws Exception {
            long t1 = System.currentTimeMillis();
            File file = new File("src/com/thread/demo/test.txt");
            StringBuilder stb = new StringBuilder();
            InputStream is = null;
            try {
                is = new FileInputStream(file);
                int read = 0;
                while ((read = is.read()) != -1) {
                    char c = (char) read;
                    stb.append(c);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            long t2 = System.currentTimeMillis();
            System.out.println("磁盘读取线程任务结束!耗时:"+(t2 - t1));
            return "DISK_OK";
        }
    }

    执行结果:

    -----------------单线程执行任务开始-----------------
    数据库连接完成!耗时:694
    磁盘文件读取结束!耗时:558
    总耗时:1253
    -----------------单线程执行任务结束-----------------
    
    -----------------多线程执行任务开始-----------------
    数据库连接线程任务结束!耗时:743
    磁盘读取任务结束!耗时:752
    总耗时:755
    -----------------多线程执行任务结束-----------------

    总结

    结果符合预期,这也说明在有频繁的IO操作时使用多线程会大大提高程序的执行效率。
    有兴趣的同学可以试一下在执行i++的情况下,多线程就一定快吗?单线程和多线程的临界值是什么?

    附:

    • 为什么Redis单线程却很快,在没有磁盘IO的情况下单核CPU绑定一块内存效率最高,Redis把读写操作都放在了CPU和内存的部分,又减少了多线程上下文 切换的过程,因此Redis即便是单线程也很快,在现代多核CPU的服务器中,往往会通过根据绑定Redis进程和CPU提高性能。
    • 《性能调优攻略》在多核CPU调优章节提到,我们不能任由操作系统负载均衡,因为我们自己更了解自己的程序,所以,我们可以手动地为其分配CPU核,而不会过多地占用CPU0,或是让我们关键进程和一堆别的进程挤在一起。在文章中提到了Linux下的一个工具,taskset,可以设定单个进程运行的CPU。
      详细可参考:https://www.cnblogs.com/blogtech/p/11742057.html
    展开全文
  • 多线程内存访问是否比单线程内存访问更?假设我们使用的是C语言.一个简单的例子如下.如果我有一个巨大的数组A,我想将A复制到与B大小相同的数组B.使用多线程比单线程更地进行内存复制吗?多少线程适合进行这种...
  • java中多线程的使用是非常频繁的,而且它的作用也是很明显的。很多人可能对于java中多线程的一些知识不是很了解,今天就来详细简述一下java多线程的优缺点,一起来看看吧。首先我们需要知道的是,java 中使用...
  • Java多线程

    2021-02-28 06:03:31
    多线程应用程序的执行都是cpu在做着快速的切换完成的。这个切换是随机的1、进程直译:正在...称之为多线程一个进程中至少一个线程开启多线程是为了同时运行多部分代码,每一个线程都自己运行的内容。这个内容...
  • Java多线程技能

    2021-03-01 08:14:53
    下面进入Java多线程的学习,首先介绍Thread类的核心方法* 线程的启动* 线程的暂停* 线程停止* 线程的优先级* 线程安全的相关问题一、进程要学习多线程就不得不提到进程,进程是什么,当我们打开windows系统的任务...
  • c++多线程简单实现

    2021-08-17 14:05:15
    文章目录一、线程和多线程线程多线程二、c++中的多线程实现1.简介2.具体实现三、代码示例四、问题 一、线程和多线程 线程 线程是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。...
  • 01 多线程工作在开始讲今天的正文之前,先给大家介绍一个概念「多线程工作」,这个概念可能的人听过,也可能的人平常工作中就是这么做的。我再来给大家讲讲这个概念,所谓的「多线程工作」就是同时做好几件事情...
  • 什么是进程?在计算中,进程是正在执行的计算机程序的一个实例。或者简单地说,运行程序也称为进程。进程意味着“在单个计算机系统中具有两个或更个CPU。...在进程中,每个进程都不同...
  • 工作中常常会出现,数据处理积压的...java的jdk中已经分装好了多线程模块,实现多线程的方式两种,一种是通过继承 Thread 类,另一种是通过实现 Runnable 接口。使用继承 Thread 类创建多线程,最大的缺点就是不...
  • 多线程的实现和使用场景

    万次阅读 2021-06-09 22:06:20
    多线程的实现和使用场景一、多线程实现方式1.1 Thread实现1.2 Runnable实现二、多线程的使用场景1.多线程使用场景1.1 多线程应该最多的场景:1.2多线程的常见应用场景:2.多线程小案列2.1 多线程计算2.2 多线程实现...
  • 当然你也可以利用多线程+CPU+轮询方式来解决问题……方法和手段是多样的,关键是自己看起来实现方便能够满足要求,代价也合适。 按照多个不同的维度(类别),来看看多线程和多进程的对比(注:因为是感性的比较,...
  • 我们知道线程的上下文切换是很消耗性能的,而第二种情况发生的则是一个任务完成再执行下一个任务,基本没线程切换,那么讲道理,难道是第二种情况下更加快了,那么就抛出个问题在单核CPU下,必要存在多线程吗?...
  • java多线程查询

    千次阅读 2021-03-05 17:06:15
    标签:由于最近工作遇到性能问题,尝试研究用多线程来实现,结果速度了好几倍下面是多线程查询的部分代码,提供给大家参考下:线程类:带返回值的类要实现Callable接口,具体业务逻辑没有实现,只是写了个空方法在...
  • 首先说下多线程出现的原因: 为了解决负载均衡问题,充分利用CPU资源.为了提高CPU的使用率,采用多线程的方式去同时完成几件事情而不互相干扰.为了处理大量的IO操作时或处理的情况需要花费大量的时间等等,比如:读写...
  • 多线程带来的问题 为什么需要多线程 其实说白了,时代变了,现在的机器都是多核的了,为了榨干机器最后的性能我们引入单线程。 为了充分利用CPU资源,为了提高CPU的使用率,采用多线程的方式去同时完成几件事情而不...
  • 为此我们可以使用多线程或者多进程来处理。 首先声明一点! 多线程和多进程是不一样的!一个是 thread 库,一个是 multiprocessing 库。而多线程 thread 在 Python 里面被称作鸡肋的存在!而没错!本节介绍的是就是...
  • 多线程和多进程通常在硬件层面上和操作系统层面上都存在线程的概念。但是这两个概念是完全不同的,是一个词汇在不同层面上的不同意思。CPU数,核心数,硬件的线程数CPU数指的是一个计算机主板上实际上卡槽中插入的...
  • autojs之多线程-Threads

    2021-07-16 21:09:44
    在这里,我就从一个小白的角度,说明一下多线程到底怎么用, 若错误,感谢指出 一、多线程使用须知 threads模块提供了多线程支持,可以启动新线程来运行脚本。 脚本主线程会等待所有子线程执行完成后才停止执行...
  • Java多线程排序使用

    2021-03-22 17:44:59
    1.先试一下我们不用多线程的情况,以快速排序为例packageadvance1;/**@Author:Gnight*@Date:2020/12/1723:32*@Description:单线程的快速排序,太多数据栈会溢出*/importjava.util.Arrays;publicclassJavaDem...
  • C中的多线程

    2021-05-22 03:38:00
    原标题:C中的多线程什么是线程?线程是进程内的单个序列流。由于线程具有某些进程属性,因此它们有时称为轻量级进程。进程和线程什么区别?线程并不是独立于其他类似的进程,因此线程与其他线程共享其代码段,...
  • 我们在执行java多线程并发程序时时候会碰到执行特别慢的场景,小伙伴们知道是什么原因导致的吗?它要怎么解决呢?下面小编就为你讲讲。前提:在某地需要开发一个应用系统,此系统主要功能是能够让一些中小型企业填写...
  • 多线程面试题整理

    2021-01-22 10:52:55
    1.多线程有什么用? 1)发挥多核CPU 的优势 随着工业的进步,现在的笔记本、台式机乃至商用的应用服务器至少也都是双核的 ,4 核、8 核甚至 16 核的也都不少见,如果是单线程的程序,那么在双核 CPU 上 就浪费了...
  • 1.用wget下载多个文件: 用wget下载,由于限制,用wget命令一次只能下载一条链接。 如果你想要下载多条链接,一条一条的下载太慢了。...2.axel多线程下载(多线程超级): axel多线程下载,真的很快。是普通下载的几倍
  • Java多线程使用场景

    2021-03-10 03:38:56
    一、几个基本概念的区分 1.进程与程序的区分 程序 进程 包含代码部分、数据部分 包含代码部分... 时候使用多线程并不是为了提高效率,而是使得CPU能够同时处理多个事件。使用场景1为什么了不阻塞主线程,启动其他...
  • Java多线程(一文看懂!)

    2021-07-28 17:36:15
    具有多线程能力的计算机因硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中
  • Qt 的4种多线程实现详解

    千次阅读 2021-02-08 19:25:53
    为何需要多线程? 1、进行耗时操作时,可以处理用户的其他输入输出。比如,如果在UI线程里面进行耗时操作,界面会不响应用户操作。 2、提升程序性能。现在的电脑一般都是多核CPU,多线程并行处理事务,可以大大...
  • Mac os 多线程

    2021-01-20 20:25:09
    如果CPU调度线程的时间足够,就造成了多线程并发执行的假象 如果线程特别多,会发生什么情况? 1.CPU在N多线程之间调度,CPU会累死,消耗大量的CPU资源 2.每条线程被调度执行的频次会降低(线程的执行效率降低) ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 724,140
精华内容 289,656
关键字:

多线程到底有多快