混合编程之多线程

【云脚本++】可以把所有代码全部挂写在网络上的强大脚本神器(支持VBS,JS多线程混合编程) 云脚本++,可以把所有代码全部挂在网络上的脚本工具。 支持JS VBS多线程混合编程。已测试,支持XP以后的系统(之前的没测试) 执行文件仅64KB就能拥有全世界,所有库都可以放网上,需要哪个加载哪个: 文档已经写了一部分(写例子太懒了,希望有人愿意边学习边代写一下哈哈,大家看附件里面的示例吧) 附件里面已经放了例子,其中3个例子最典型,学好了基本就会用了 附件里面已经放了VBS版的控制台调试器。直接输入代码即可,也可以用来调试别人的DLL(支持按键精灵官方DLL)... 阅读详情

前言: 小白一个, 没有系统性的学习过python网络通信,只是单纯的cv加修改代码,仅作留念以及参考用,感谢互联网博主们和bito插件,使得chatGPT得以免费使用.
另外该多线程传输图片的速度比没有多线程执行还满,后续不对python服务端做优化,而改为C++服务端实现.写出来继续再分享把

前篇博客地址

python客户端

采用生存者消费者模式、模式2和joinablequeue库.
客户端实现还是比较简单的,麻烦在server端

import pickle
import time
from multiprocessing import Process, JoinableQueue
from queue import Queue

from multiprocessing.connection import Client, Listener

from client_apart import draw_box
from image import plot_boxes, img_encode
import os
from natsort import ns, natsorted

host = 'localhost'
port = 9006
total_time = 0


def img_product(img_queue, path, path_mode='image'):
    if path_mode == 'image':
        img = img_encode(path)
        img_obj = {'frame_num': 1, 'image': img}  # need frame_num?
        img_queue.put(img_obj)
    elif path_mode == 'dir':
        dir_list = os.listdir(path)
        files = natsorted(dir_list, alg=ns.PATH)  # 顺序读取文件名
        i = 1
        for filename in files:
            img_path = path + '/' + filename
            img = img_encode(img_path)
            img_obj = {'frame_num': i, 'image': img}  # need frame_num?
            i += 1
            img_queue.put(img_obj)
        img_queue.put({'frame_num': 0, 'image': "end"})  # end signal 
    img_queue.join()


def server_consumer(img_queue):
    # 1. send data
    while True:
        img_obj = img_queue.get()
        if img_obj is None:
            client.close()  # avoid connection-reset-by-peer
            break  # exit end
        data_bytes = pickle.dumps(img_obj)
        start = int(round(time.time() * 1000))
        start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        client.send(data_bytes)  # 40ms/per send img
        # print('send cost time: ', (end - start))
        img_queue.task_done()
        try:
            det_result = client.recv()
            end = int(round(time.time() * 1000))
            end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
            print('recv cost time: ', (end-start))
        except EOFError:
            break
        det_result = pickle.loads(det_result)
        draw_box(det_result, img_obj)


if __name__ == '__main__':
    client = Client((host, port))
    Listener()
    img_dir = './data'
    one_img = './data/Enterprise001.jpg'
    mode = 'dir'

    img_jq = JoinableQueue()
    producer = Process(target=img_product, args=(img_jq, img_dir, mode,))
    consumer = Process(target=server_consumer, args=(img_jq,))
    consumer.daemon = True  # set daemon but not set join()

    producer.start()
    consumer.start()

    producer.join()

Python服务端

此处接受图片和发送结果线程处在一个类内,使得可以共享一个Listener,来自chatGPT的idea.

from ctypes import *
import ctypes
import time
import pickle
from PHeader import *

import cv2
import numpy as np
from multiprocessing.connection import Listener
from multiprocessing import JoinableQueue, Process, Queue, connection
import threading


class ListenerThread(threading.Thread):
    def __init__(self, address, data_handler, lib):
        super().__init__()
        self.daemon = True
        self.address = address
        self.data_handler = data_handler
        self.lib = lib
        self.listener = Listener(self.address, backlog=5)
        self.conn = None
        self.is_running = True
        
    def run(self):
        print('Listening on', self.listener.address)
        while self.is_running:
            try:
                self.conn = self.listener.accept()  # ready to accept data continually
                print('Connected by', self.listener.last_accepted)
                t1 = threading.Thread(target=self.receive_data)
                t2 = threading.Thread(target=self.send_result)
                t1.start()
                t2.start()
                t1.join()
                t2.join()
            except OSError as e:
                if e.errno != 98:  # Address already in use
                    raise
                print('Address already in use, retrying in 1 second...')
                time.sleep(1)
        

    def destroy_Model(self):  # when qt send a signal
        self.lib.interface_destoryThread()

    def receive_data(self):
        time_cost1 = 0
        while True:
            try:
                start = int(round(time.time() * 1000))
                received_bytes = self.conn.recv()  # recv Client data
                end = int(round(time.time() * 1000))
                print('recv time cost: ', end-start)
                time_cost1 += end-start
                img_dict = pickle.loads(received_bytes)
                if img_dict["frame_num"] == 0:
                    print("receive's thread already receive all data, close thread!!")
                    print("recv: ", time_cost1)
                    self.lib.interface_setEmptyFlag()  # make send thread break
                    break
                img_dict['image'] = cv2.imdecode(img_dict['image'], cv2.IMREAD_COLOR)
                self.data_handler.sendImgtoC(self.lib, img_dict, 0)  # prepare to send img
            except EOFError:
                print('Connection closed')
                self.conn.close()
                break

    def send_result(self):
        time_cost1 = 0
        time_cost2 = 0
        while True:
            self.lib.interface_getClsQueue.restype = ObjClassifyOutput
            start = int(round(time.time() * 1000))
            output = self.lib.interface_getClsQueue()  # get result from model
            end = int(round(time.time() * 1000))
            time_cost1 += end-start
            print('get cls time cost: ', end-start)
            if output.object_list.object_num >= 0:
                cls_result = self.data_handler.CtoP(output)
                cls_result = pickle.dumps(cls_result)
                start = int(round(time.time() * 1000))
                self.conn.send(cls_result)
                end = int(round(time.time() * 1000))
                print('send time cost: ', end-start)
                time_cost2 += end-start
            elif output.object_list.object_num == -1:   # queue is empty for now
                time.sleep(0.04)
                continue
            elif output.object_list.object_num == -2:   # all data is classify
                print("send's thread alreay handle all data, close thread!!")
                print("cls: ", time_cost1, ' send: ', time_cost2)
                # self.close()
                break

    def close(self):  # useless for now
        self.conn.close()
        # self.listener.close()
        self.run()


class DataHandler:
    def __init__(self):
        self.data = None

    def CtoP(self, output):  # 将模型结果解析为python列表
        # [cv_object_list: [cv_object: [cv_box: [] ]]]
        cv_object_list = []
        cls_out = []
        obj_list = output.object_list
        if obj_list.object_num != 0:
            for i in range(obj_list.object_num):
                cv_box = []
                cv_object = []
                obj = obj_list.object[i]
                # bbox
                cv_box.append(obj.bbox.left_top_x)
                cv_box.append(obj.bbox.left_top_y)
                cv_box.append(obj.bbox.w)
                cv_box.append(obj.bbox.h)
                cv_object.append(cv_box)
                # classes/objectness/prob
                cv_object.append(obj.classes)
                cv_object.append(obj.objectness)
                prob = POINTER(c_float)(obj.prob)
                cv_object.append(prob.contents.value)
                # cv_object
                cv_object_list.append(cv_object)
            cv_object_list.append(obj_list.object_num)

            # cv_object_list
            cls_out.append(cv_object_list)
        return cls_out

    def sendImgtoC(self, lib, img_dict, i):
        lib.interface_receive.argtypes = [PythonMat]
        # 1. combine info to img
        pi = PythonMat()
        pi.frame_num[0] = img_dict["frame_num"]
        img = img_dict['image']
        # 1.1 set width/height
        PARAM = c_int * 32
        height = PARAM()
        height[0] = img.shape[0]
        pi.height = height
        width = PARAM()
        width[0] = img.shape[1]
        pi.width = width
        # 1.2 set Mat
        frame_data = np.asarray(img, dtype=np.uint8)
        frame_data = frame_data.ctypes.data_as(c_char_p)
        pi.frame[0] = frame_data
        # 2. send img to detect model
        lib.interface_receive(pi)


if __name__ == '__main__':
    address = ('localhost', 9006)
    data_handler = DataHandler()
    ll = ctypes.cdll.LoadLibrary
    lib = ll("../../lib/libDetClsController.so")  # create a C lib
    listener_thread = ListenerThread(address, data_handler, lib)
    listener_thread.start()
    try:
        det_process = threading.Thread(target=lib.interface_initDetectImage)
        cls_process = threading.Thread(target=lib.interface_initClassifyImage)
        det_process.start()
        cls_process.start()
        det_process.join()  # need a break signal
        cls_process.join()
    except KeyboardInterrupt:
        # 程序被强制关闭
        print('Program terminated')
        # 关闭ListenerThread对象
        listener_thread.is_running = False
        listener_thread.join()
    finally:
        # 关闭ListenerThread对象
        listener_thread.is_running = False
        listener_thread.join()

C++模型调用

此处涉及其他人员写的代码,做修改处理

typedef struct 
{
int width[CV_MAX_BATCH_SIZE];
int height[CV_MAX_BATCH_SIZE];
char* frame[CV_MAX_BATCH_SIZE]; 
int frame_num[CV_MAX_BATCH_SIZE]; 
}PythonMat;

// 多线程控制相关
mutex mtxQueueDet;  // mutex for detect queue
mutex mtxQueueImg;  // mutex for image queue
mutex mtxQueueCls;  // mutex for classify queue
mutex mtxif;
queue<> queueDetOut;// Det output queue
queue<> queueClsOut;// Det classify queue
queue<cv::Mat> queueMat;
bool DetectFlag = true;
bool ClassifyFlag = true;
bool empty_flag = false;

void receiveImg(PythonMat &img)
{
    cv::Mat frame(img.height[0], img.width[0], CV_8UC3, img.frame[0]);

    mtxQueueImg.lock();
    queueMat.push(frame);
    cout << "frame num: "<<  img.frame_num[0] << endl;
    mtxQueueImg.unlock();
}

void DetectImage()
{
    Detect detect_output;
    Detect detectmodel;
    detectmodel.Init(config_path, 1);
    cv::Mat frame;
    while(1)
        { 
            if (queueMat.empty()) 
            {
                if(!DetectFlag)
                {
                    break;
                }
                usleep(2000);
                continue;
            }
            mtxQueueImg.lock();
            frame = queueMat.front();
            queueMat.pop();
            mtxQueueImg.unlock();

            detect_output = detectmodel.Run(detect_input);
            
            // lock
            mtxQueueDet.lock();
            queueDetOut.push(detect_output);
            cout << "detect run !!" << endl;
            mtxQueueDet.unlock();
        }
    return;
}

void ClassifyImage()
{
    Classify objclassify;
    objclassify.Init(config_path, 1);
    ClassifyInput input;
    ClassifyOutput output;
    cv::Mat frame;
    while(1)
    {
        if (queueDetOut.empty()) 
        {
            if(!ClassifyFlag)
            {
                break;
            }
			usleep(2000);
            continue;
		}
        
        mtxQueueDet.lock();
        detect_result = queueDetOut.front();
        queueDetOut.pop();
        mtxQueueDet.unlock();
        
        output = objclassify.Run(input);
        
        mtxQueueCls.lock();
        queueClsOut.push(output);
        mtxQueueCls.unlock();
    }
    return;
}


ClassifyOutput getClsQueue(){
    ObjClassifyOutput output;
    if (queueClsOut.empty()){
        usleep(2000);
        output.object_list.object_num = -1;  // -1 is now empty; -2 is all empty 
        if (empty_flag){
            output.object_list.object_num = -2;
            empty_flag = false;
        }
        return output;
    }
    mtxQueueCls.lock();
    output = queueClsOut.front();
    queueClsOut.pop();
    cout << "cls_out pop " << output.object_list.object_num << endl;
    mtxQueueCls.unlock();
    return output;
    
}

extern "C" {
int i = 0;
void interface_initDetectImage(){
    // if (flag ) exit thread detect/classify 
    DetectImage();
}
void interface_initClassifyImage(){
    ClassifyImage();
}
void interface_receive(PythonMat &img){
    printf("run %d times\n", i++);
    receiveImg(img); 
}
void interface_setEmptyFlag(){
    empty_flag = true;
}
void testThread(){
    printf("C++ method!!\n");
}
ClassifyOutput interface_getClsQueue(){
    ClassifyOutput output;
    output = getClsQueue();
    return output;
}

void interface_destoryThread(){
    DetectFlag = false;
    ClassifyFlag = false;
}
}

至此这个混合编程项目结束,但是server接收到client耗时居然300ms,需要优化…

参考博客

  1. 共享内存
  2. Address already in use
  3. connection-reset-by-peer 我是把client.close()加上就不报错
  4. (最终版)linux下python和c++相互调用共享内存通信
  5. 常见python通信错误
    1. EOFError
    2. OSError: handle is closed
    3. Broken pipe
pybind11混合编程合规性验证:从内存安全到多线程的实战指南 在C++与Python混合编程中,通过pybind11等工具进行接口绑定是提升性能的常见手段。其核心原理在于实现两种语言间对象模型与内存管理的无缝转换。这项技术的价值在于能让Python生态便捷调用高性能C++代码,广泛应用于机器学习、科学计算和高频交易等场景。然而,这种便利性背后潜藏着内存泄漏、类型转换错误和线程安全等深层次隐患,尤其在处理复杂对象生命周期和全局解释器锁(GIL)时问题频发。为确保模块在生产环境中的稳定性,必须建立系统的合规验证体系,针对**内存与生命周期管理**、**类型系统契约**等关 阅读详情

相关推荐

C#与Halcon混合编程避坑指南:内存管理、多线程与性能优化实战

在工业自动化与机器视觉领域,C#因其高效的开发效率和.NET生态的丰富性,常被用于构建上位机软件。而Halcon作为业界领先的机器视觉算法库,提供了强大的图像处理能力。两者结合时,核心挑战在于托管代码与非托管资源的高效协同。其原理涉及.NET垃圾回收机制与Halcon底层C++引擎的内存管理差异,关键在于理解Halcon对象作为非托管资源的包装器本质。这一协同的技术价值在于,能够构建出既具备快速开发迭代能力,又拥有强大、稳定视觉处理核心的工业级应用。典型的应用场景包括视觉检测、定位、测量等系统。本文聚焦于两

weixin_34199764的博客 348

OpenCV-Python cv2.imdecode()和cv2.imencode() 图片解码和编码

cv2.imdecode()函数从指定的内存缓存中读取数据,并把数据转换(解码)成图像格式;主要用于从网络传输数据中恢复出图像。 cv2.imencode()函数是将图片格式转换(编码)成流数据,赋值到内存缓存中;主要用于图像数据格式的压缩,方便网络传输。 imdecode()使用 从网络读取图像数据并转换成图片格式: # -*- coding: utf-8 -*- import num...

云中寻雾的博客 9270

VsCode安装和配置c/c++环境(超完整,小白专用)

1. vsCode配置C/C++环境 1. vsCode下载和安装 1. 下载Microsoft vsCode [vsCode在这里插入图片描述官网下载: https://code.visualstudio.com/ 2. 安装vsCode 3. 下载中文插件 我这边已经下载过,刚卸载但是没有重启编译器,所以界面显示了中文 2. MinGW编译器下载和配置 1. 下载MinGW 在线安装——下载MinGW-W64-install.exe(速度慢) 下载压缩包——×86_64-posix-

黄化的多多 60万+

【cv2.imread】与【cv2.imdecode】用法

对于路径中含有中文的图像,直接用cv2.imread读取会报错,上次看到有大佬使用cv2.imdecode就可以正常读取,有点好奇,所以今天来记录下二者用法和区别。

向上生长的程序媛的博客 1万+

CCS安装教程图解说明:可视化步骤快速上手

通过清晰的图解步骤详解CCS安装过程,帮助新手快速掌握环境搭建。结合ccs安装教程与实际操作界面,每一步都直观易懂,大幅降低学习门槛,提升配置效率。

weixin_42611310的博客 960

手把手用LTspice仿真运算放大器电路:从虚短虚断到频响分析

本文通过LTspice仿真软件,手把手教你从搭建基础运算放大器电路开始,验证虚短虚断原理,并逐步深入频响分析与稳定性探究。内容涵盖反相/同相放大器、积分器仿真,导入真实运放模型进行蒙特卡洛分析,以及利用交流分析和波特图评估电路性能,帮助工程师和学生将抽象理论转化为直观、可测量的仿真结果,提升电路设计实战能力。

salt9的博客 1094

C++多线程多进程服务器muduo函数查询表(一)

本文记录下本人在写服务器过程中遇到的一些相关函数,其具体详解参考给定的参考网址,本文只是做个记录,便于以后再次查询相关函数: 1.(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) 允许重用地址!!!消除bind时"Address already in us...

weixin_38428439的博客 440

【华为OD机试】贪吃蛇(python, java, c++, js)

贪吃蛇是一个经典游戏,蛇的身体由若干方格连接而成,身体随蛇头移动。蛇头触碰到食物时,蛇的长度会增加一格。蛇头和身体的任一方格或者游戏版图边界碰撞时,游戏结束。 下面让我们来完成贪吃蛇游戏的模拟。 给定一个NM的数组arr,代表NM个方格组成的版图,贪吃蛇每次移动一个方格。 若arr[i][j] == H,表示该方格为贪吃蛇的起始位置; 若arr[i][j] == F,表示该方格为食物, 若arr[i][j] == E,表示该方格为空格。 贪吃蛇初始长度为1,初始移动方向为向左。

A_D_I_D_A_S的博客 540

c/c++多线程编程与无锁数据结构漫谈

本文主要针对c/c++,系统主要针对linux。本文引述别人的资料均在引述段落加以声明。

songtzu的专栏 3884

SAP S4 HANA:按单生产(MTO)场景中的COGS分割

本文介绍了SAP S/4HANA中提供的各种选项,以在销售订单/商品是按订单生产(以下称为MTO)生产流程中的成本对象时,在基于帐户的COPA环境中报告COGS分割信息。

adam915的博客 1191

第十六章:载板制造工艺大全

本文详细介绍了IC载板制造的完整工艺流程,主要包括开料、钻孔、孔金属化、图形形成、层压、表面处理、成型和电测8大工序。每个工序都列出了关键参数和质量控制要点,如开料尺寸精度±0.1mm、激光钻孔孔径50-150μm、孔金属化铜厚20-30μm、图形形成线宽15μm±2μm等。文中还特别说明了MSAP工艺和沉金等特殊工艺的详细流程,以及成型精度和电测标准等关键指标。全文通过流程图和参数表格直观展示了IC载板制造的核心工艺和质量要求。

Open 183

QT中C/C++混合编程 openMP多线程遇到的坑

这几天有个项目需要写个界面,由于前期的代码是用C写的,因此毅然决定使用QT来做个界面。 背景: 原始后端程序使用纯C语言编写 程序中使用了openMP多线程加速 QT原生为C++编程,因此就成了C/C++混合编程 编译器使用的MinGW(gcc–5.3.0),支持openMP 在QT中,如果要使用openMP进行加速,需要再pro文件中加入(MinGW gcc版本的): /* 注意,下面这个f...

longzhen1825的博客 1641

QNX侧触摸屏(TP)Bringup流程与源码深度解析

QNX侧TP Bringup的核心在于构建。

QQ_778132974的博客 477

Python多线程与异步IO混合编程实践:解决“There is no current event loop“问题

方案适用场景优点缺点纯同步(requests)低并发、简单场景代码简单性能差异步+多线程高并发网络请求高性能需管理事件循环多进程CPU密集型+高IO混合任务绕过GIL进程开销大优先使用同步代码(除非性能瓶颈明确)异步+多线程适合高并发HTTP请求Java的异步方案更优雅(推荐通过合理选择方案,可以避免错误,并构建高性能的并发应用。

私聊前往站内信:https://i.youkuaiyun.com/#/msg/chat/weixin_44976692 1万+

SAP_SD模块更改销售订单定价简介

方式优点缺点适用场景MASS简单,无需开发不能触发重新定价逻辑修改特定字段BDC (VA02)可模拟人工操作维护复杂,速度慢少量订单批改稳定、安全需ABAP支持推荐用于批量重新定价VBOF标准重算返利仅适返利类条件返利更新通过本文,我们了解了 SAP 的[SAP_SD模块更改销售订单定价]的基本概念和应用方法。希望这些信息能帮助您在实际工作中更好地使用 SAP 系统,提高工作效率和管理水平。更多SAP文章请点击更多SAP文章更新,大家一起学习进步!!!

我小时候很黑的博客 706

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

asyncio 混合编程:多线程和多进程的正确打开方式

需求方案关键 API多个独立的事件循环asyncio + 多线程CPU 密集型计算asyncio + 多进程IO 密集型任务纯 asynciogather()asyncio + 多线程:多事件循环架构,适合隔离业务模块asyncio + 多进程:突破 GIL,适合 CPU 密集型计算核心思想:让异步代码无缝整合同步世界,既保留异步的高效,又解决单线程的瓶颈。

workind的博客 1047

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

第13课:asyncio|多线程|多进程混合编程实战

本课程深入探讨Python中异步编程(asyncio)、多线程与多进程的混合应用场景,旨在解决高并发、计算密集型及I/O密集型任务的性能优化问题。通过实际案例演示如何结合三者优势,规避全局解释器锁(GIL)限制,提升程序效率。asyncio适用于高并发I/O操作,通过事件循环和非阻塞调用实现轻量级并发;多线程适合处理I/O密集型任务,但需注意线程安全;多进程则绕过GIL,充分利用多核CPU处理计算密集型任务。

Java 后端|Python|AI 实战博主。专注微服务、分布式、大模型应用开发,输出可落地项目、源码剖析与面试总结。用通俗文字拆解技术难点,记录开发路上的学习与踩坑,期待和大家一起交流成长。 692

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

多线程nxtimer定时器改单线程uv_timer定时器 C C++混合编程

这段代码实现了从nxtimer到uv_timer定时器的转换,并处理了C/C++混合编程的问题。核心功能是实现单线程定时器。 主要修改点包括: 将nxtimer替换为libuv的uv_timer_t,避免多线程问题 使用C++类封装定时器逻辑(BrightnessTimer) 通过extern "C"提供C语言兼容接口 使用智能指针管理定时器资源 实现了状态管理函数供C代码调用 定时器逻辑被封装在BrightnessTimer类中,通过handleTou

Obamaaaa的博客 349

C++和QML混合编程_C++端后台多线程处理

C++和QML混合编程_C++端后台多线程处理

会选科技的博客 719

matlab与C语言混合编程之openmp多线程

作为非MATLAB控,却选择MATLAB进行项目的研究,主要还是因为MATLAB使用起来实在太方便了。因此,它的缺陷也很明显,效率不高。内部定义的任何函数,如果我们使用C语言重写,效率一般都能得到一定的提升。     而对于复杂度更高的算法,则需要进一步使用并行编程的方法,比如多线程。那么,在我们使用MATLAB进行项目的主体框架设计后,如何调用使用C语言编写的代码呢?首先,C语言程序肯定需

海之蓝 4543

MPI与多线程(如OpenMP)混合编程注意事项与性能优化

通过合理结合MPI的进程级并行和OpenMP的线程级并行,可以充分利用现代集群的计算资源,实现更高的并行效率和更好的性能扩展性。

weixin_42849849的博客 1860

【C++ 与Qt 线程】C++ std::thread 与Qt qthread多线程混合编程

C++与Qt深度融合:高效设计多线程应用框架

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 5357

鸿蒙跨端开发:ArkTS与Java混合编程的性能边界测试

鸿蒙操作系统(HarmonyOS)由华为开发,是一款面向全场景、多终端的分布式操作系统。其目标是实现不同设备间的无缝连接和协同工作,包括手机、平板、智能电视、智能穿戴设备等。与传统的Android或iOS不同,鸿蒙采用了一种分布式架构,允许开发者在多个终端之间共享和同步数据。??在鸿蒙跨端开发中,ArkTS与Java的混合编程模式为开发者提供了更高效、灵活的开发方案。通过合理的性能测试与优化,可以确保应用在不同平台上的高效运行,满足用户日益增长的需求。??

xxx_0147的博客 927

《Python实战进阶》No42: 多线程与多进程编程详解(下)

本文将提供多线程和多进程协同实战案例,切实提高并发编程的能力!!!在Python中,多线程与多进程是实现并发编程的核心技术。本集聚焦两者的底层原理与实战差异,结合AI大模型场景(如数据预处理与分布式训练),演示如何通过混合编程提升性能。我们将通过真实代码案例,对比多线程与多进程在图像处理任务中的效率,并探讨如何规避GIL的限制。

yweng18的博客 1541

【python3】多进程与多线程的混合编程

【代码】【python3】多进程与多线程的混合编程。

言之。 1万+
上一篇: python 与python通信、python调用c++ 动态库返回检测结果
下一篇: 混合编程python与C++
AlexNett
博客等级 码龄9年 562粉丝 40原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值