Multi-threaded applications and asynchronous I/O(翻译)

2023-11-08

此文章使用Goolge进行翻译学习使用。原文网址为:http://libusb.sourceforge.net/api-1.0/mtasync.html

本文章是为了调查:(libusb_bulk_transfer()会阻塞,阻塞时间长达60s的问题)[http://blog.csdn.net/encourage2011/article/details/78760276]


libusb是一个线程安全的库,但是应用于与多线程中的libusb交互的应用程序必须要注意。

必须解决的根本问题是,所有的libusb I/O 都围绕着通过poll()/select()系统调用来监视文件描述符。这是直接暴露在异步接口,但需要着重注意的是:同步接口是在异步接口的顶部实现,因此同样的考虑适用。

问题是,如果两个或多个线程同时对libusb的文件描述符调用poll()或select(),那么当事件到达时,只有其中一个线程会被唤醒,另一个线程将完全没有意识到发生了什么事情。

考虑下面的伪代码,它提交一个异步传输,然后等待它完成。 这种风格是你可以在异步接口之上实现一个同步接口的一种方式(而且libusb也有类似的功能,尽管由于本页面所介绍的复杂性而使得它更为先进)。

void cb(struct libusb_transfer *transfer)
{
    int *completed = transfer->user_data;
     *completed = 1;
}
void myfunc() {
    struct libusb_transfer *transfer;
    unsigned char buffer[LIBUSB_CONTROL_SETUP_SIZE] __attribute__ ((aligned (2)));
    int completed = 0;
    transfer = libusb_alloc_transfer(0);
    libusb_fill_control_setup(buffer,
        LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, 0x04, 0x01, 0, 0);
    libusb_fill_control_transfer(transfer, dev, buffer, cb, &completed, 1000);
    libusb_submit_transfer(transfer);
    while (!completed) {
        poll(libusb file descriptors, 120*1000);
        if (poll indicates activity)
            libusb_handle_events_timeout(ctx, &zero_tv);
    }
    printf("completed!");
    // other code here
}

在这里,我们正在序列化一个异步事件的完成情况与某一条件相反,该条件是完成一个特定的传输。
poll()循环具有很长的超时时间,以便在没有任何事情发生的情况下(可以合理地无限制)最小化CPU使用率。

如果这是轮询libusb的文件描述符的唯一线程,则没有问题:另一个线程将不会吞下我们感兴趣的事件。另一方面,如果有另一个线程轮询相同的描述符 ,有可能会收到我们感兴趣的事件。在这种情况下,myfunc()只会意识到,在循环的下一次迭代中,传输已经完成,最多120秒后。
显然,两分钟的延迟是不可取的,甚至不能考虑使用短暂的超时来解决这个问题!

这里的解决方案是确保没有两个线程同时轮询文件描述符。一个原始的执行会影响库的能力,所以libusb提供下面的方案以确保不丢失功能。

在我们进一步讨论之前,值得一提的是,所有libusb包装的事件处理程序都完全遵循下文所述的方案。 这包括libusb_handle_events()及其相关函数以及所有同步I/O函数。libusb已把用户的头痛的事隐藏起来。


来自多个线程的libusb_handle_events()

即使只使用libusb_handle_events()和同步I/O函数,仍然可能有竞争条件。 你可能会试图用libusb_handle_events()来解决上述问题:

libusb_submit_transfer(transfer);
while (!completed) {
    libusb_handle_events(ctx);
}
printf("completed!");

然而,完成检查和libusb_handle_events()获取事件锁之间存在竞争,所以另一个线程可能已经完成了传输,导致该线程挂起,直到发生超时或其他事件。 另请参见提交6696512aade99bb15d6792af90ae329af270eba6,它在libusb的同步API实现中修复了这个问题。

修正这个竞争需要检查变量完成后才采取事件锁定,这打破了只是调用libusb_handle_events()的概念,而不必担心锁定。 这就是为什么libusb-1.0.9引入了新的
libusb_handle_events_timeout_completed()libusb_handle_events_completed()函数,它们在获取锁之后处理完成检查:

libusb_submit_transfer(transfer);
while (!completed) {
    libusb_handle_events_completed(ctx, &completed);
}
printf("completed!");

这很好地解决了我们的例子中的竞争。 请注意,如果您只想提交一个传输并等待完成,那么使用其中一个同步I/O函数就容易多了。


事件锁

问题是,当我们考虑到libusb公开文件描述符以允许将异步USB I / O集成到现有的主循环这一事实时,可以有效地让你在libusb的背后做一些工作。 如果你使用libusb的文件描述符并将它们传递给poll()/ select(),你需要知道有可能出现相关的问题。

首先要介绍的概念是事件锁。 事件锁用于序列化要处理事件的线程,以便一次只有一个线程处理事件。

在轮询libusb文件描述符之前,必须使用libusb_lock_events()来锁定事件锁定。 您必须在使用libusb_unlock_events()中止poll()/select()循环后立即释放锁。


让其他线程为你工作

尽管事件锁是解决方案的重要组成部分,但仅靠个人解决方案还不够。 你可能想知道下面是否足够…

libusb_lock_events(ctx);
while (!completed) {
    poll(libusb file descriptors, 120*1000);
    if (poll indicates activity)
        libusb_handle_events_timeout(ctx, &zero_tv);
}
libusb_unlock_events(ctx);

…而答案是,事实并非如此。 这是因为在上面显示的代码中的传输可能需要很长时间(比如30秒)才能完成,并且在传输完成之前不会释放锁定。

另一个具有类似代码的线程想要做事件处理,可能会在几毫秒后完成一次传输。 尽管有这么快的完成时间,但这个线程无法检查它的传输状态,直到上面的代码由于争用锁而结束(30秒后)。

为了解决这个问题,libusb提供了一个机制来确定另一个线程何时处理事件。 它还提供了一种机制来阻塞你的线程,直到事件处理线程完成一个事件(并且这种机制不涉及轮询文件描述符)。

在确定另一个线程正在处理事件之后,使用libusb_lock_event_waiters()获取事件等待者锁。然后重新检查其他线程是否还在处理事件,如果是,则调用libusb_wait_for_event()

libusb_wait_for_event()会让您的应用程序进入休眠状态,直到事件发生,或者直到线程释放事件锁定。当这些事情发生时,你的线程被唤醒,并且应该重新检查它正在等待的状态。它还应该重新检查另一个线程是否正在处理事件,如果没有,它应该开始处理事件本身。

伪代码如下:

retry:
if (libusb_try_lock_events(ctx) == 0) {
    // we obtained the event lock: do our own event handling
    while (!completed) {
        if (!libusb_event_handling_ok(ctx)) {
            libusb_unlock_events(ctx);
            goto retry;
        }
        poll(libusb file descriptors, 120*1000);
        if (poll indicates activity)
            libusb_handle_events_locked(ctx, 0);
    }
    libusb_unlock_events(ctx);
} else {
    // another thread is doing event handling. wait for it to signal us that
    // an event has completed
    libusb_lock_event_waiters(ctx);
    while (!completed) {
        // now that we have the event waiters lock, double check that another
        // thread is still handling events for us. (it may have ceased handling
        // events in the time it took us to reach this point)
        if (!libusb_event_handler_active(ctx)) {
            // whoever was handling events is no longer doing so, try again
            libusb_unlock_event_waiters(ctx);
            goto retry;
        }
        libusb_wait_for_event(ctx, NULL);
    }
    libusb_unlock_event_waiters(ctx);
}
printf("completed!\n");

上面代码的看来可能表明,这只能支持一个事件等待器(因此总共有2个竞争线程,另一个做事件处理),因为事件等待器似乎已经获取到事件等待锁等待事件触发。 但是,系统确实支持多个事件等待器,因为libusb_wait_for_event()实际上在等待时放下了锁,并在继续之前再次获取它。

我们现在已经实现了可以动态处理没有人处理事件的情况的代码(所以我们应该自己动手实现它),还可以处理另一个线程正在进行事件处理的情况(所以我们可以搭载它们)。 它也可以处理两者的组合,例如,另一个线程正在进行事件处理,但是无论出于何种原因,在满足条件之前停止这样做,所以我们接管事件处理。

上述伪代码中引入了四个函数。 他们的重要性应该从上面显示的代码中显而易见。

  • libusb_try_lock_events ()是一个非阻塞函数,它试图获取事件锁定,但是如果它被竞争则返回失败代码。

  • libusb_event_handling_ok()检查你的线程是否正在执行事件处理。有时候,libusb需要中断事件处理程序,这是如何检查你是否被中断的方法。如果这个函数返回0,正确的行为是让你放弃事件处理锁,然后重复这个循环。下面的libusb_try_lock_events()会失败,所以你会成为一个事件等待器。欲了解更多信息,请阅读下面的全文。

  • libusb_handle_events_locked()libusb_handle_events_timeout()的变体,你可以在保持事件锁定的情况下调用它。libusb_handle_events_timeout()本身实现了和上面类似的逻辑,所以当你“在libusb的后面工作”的时候一定不要调用它,就像这里的情况一样。

  • libusb_event_handler_active()确定是否有人正在持有事件锁。

你可能想知道为什么没有函数来唤醒在libusb_wait_for_event()上被阻塞的所有线程。这是因为libusb可以在内部执行这个操作:当有人调用libusb_unlock_events()或传输完成时(在回调完成后),它会唤醒所有这样的线程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Multi-threaded applications and asynchronous I/O(翻译) 的相关文章

  • 使用“设备过滤器”解决方案时如何处理 Android 上的 USB 权限对话框事件?

    当我将 已知 USB 设备连接到 Android 手机时出现的自动权限对话框中 用户按下 确定 或 取消 时 我试图处理该事件 我正在使用 android usb host 库 可以在 Android 手机和设备之间发送和接收 此外 我使用
  • USB 存储设备的无代码 kext

    在一个项目中 我最近尝试通过 osx 10 9 5 上的 libusb 访问 USB 存储设备 但显然一些默认驱动程序声称该设备 我无法访问它 当我尝试通过 libusb 访问它时 它返回我有 权限不足 当我在 Ubuntu 中执行 jav
  • 如何在 OSX Catalina (10.15) 中使用 Wireshark 捕获 USB 流量

    我正在尝试让 Wireshark USB 捕获工作 显然 Catalina 之前的技巧就是调出界面 以便 Wireshark 可以看到它 https forums developer apple com thread 95380 https
  • python的跨平台usb模块?

    我有兴趣在 python 中使用 USB 设备进行一些跨平台工作 关于可以执行此类操作的模块有任何提示或建议吗 我浏览了 SF 和 googlecode 但运气不佳 thanks ct PyUSB http pyusb berlios de
  • 使用 python 检测 Windows 10 上的 USB 设备插入

    我无法获取以下代码检测USB设备插入 http timgolden me uk python win32 how do i detect device insertion html在我的 Windows 10 64 位 计算机上使用 Pyt
  • ubuntu中libusb.h和usb.h有什么区别?哪一个更好?

    我是 libusb c 编程的新手 我应该知道 libusb h 和 usb h 之间的区别吗 我已经在 Ubuntu xenial 中安装了它们 并带有 libusb 1 0 0 dev 和 libusb dev 包 哪一个更好 它们来自
  • 是否有可通过 USB 密钥在 Mac OS X 10.6 上运行的便携式 python 解释器?

    我一直在努力寻找一个可以通过工作计算机上的 USB 密钥运行的便携式解释器 Work comp 运行的是 Mac OS X 10 6 环境相当受限 无法访问终端 无法安装应用程序 但我知道便携式应用程序可以从 USB 驱动器运行 我一直在使
  • Android 到 PC USB 读/写

    我在 PC 上有一个程序 从 USB 接收字符串输入 旧程序 我有一个 Android 4 X 平板电脑 需要向在 PC 上运行的程序提供 USB 上的字符串输入 当我在 Android 上使用示例代码时 以下代码给出了空的哈希图 PC 在
  • USB 调试 - 安装 Epson Moverio 驱动程序时出现问题

    Android 开发新手 我使用以下命令制作了一个小应用程序Eclipse http en wikipedia org wiki Eclipse 28software 29在虚拟 Android 设备上完美运行 现在 我想在真实的设备上运行
  • brew 安装 libusb 链接失败

    我正在安装libusb with brew在我的 Mac 中 酿造安装libusb 链接步骤失败 如下所示 Error The brew link step did not complete successfully The formula
  • Android:与充当主机的 USB 设备通信

    我们制作了一个可以充当 USB 主机或从机并处理其接收到的数据的设备 我想使用Android手机通过USB向其发送数据 我的研究使我得出结论 如果不修改硬件或操作系统 我们就无法在 USB 主机模式下使用 Android 设备 这样做不是一
  • libv4l2:打开流时出错:设备上没有剩余空间

    我尝试为 opencv 获取立体声对 我将 Logitech B910 和 Logitech C910 网络摄像头连接到 USB 但有这个错误 我玩弄了怪癖参数并设置outfmt mjpeg在mplayer中 但又出现此错误 在哪里可以找到
  • Windows 8.1 上的 Pyusb - 没有可用的后端 - 如何安装 libusb?

    使用 pyinstaller 3 1 python 2 7 9 和 tkinter 尝试使用 pyusb 而不是 pyserial 但没有可用的后端 调查Windows 上的 Pyusb 没有可用的后端 https stackoverflo
  • 如何查找USB盘符?

    我正在编写一个安装程序来将应用程序安装到 USB 驱动器 该应用程序只能从 USB 驱动器使用 因此它可以通过自动选择要安装的 USB 驱动器来为用户节省额外的步骤 我可能会尝试使用 Nullsoft 或 MSI 进行安装 但由于我最熟悉
  • 如何在 C# 中从 USB 令牌读取证书

    我有一个 USB 令牌 其中包含一个加密证书 该证书具有公钥和私钥 现在我想用 C 创建一个应用程序 通过它我可以找到可访问的证书信息 当我插入 USB 令牌时 它会被检测到 但计算机部分上没有显示任何驱动器 就像闪存驱动器一样 如何从 U
  • 如何使用 python / pywinusb 将 hid 数据发送到设备?

    我正在尝试使用 pywinusb 将输出报告发送到 pic18f4550 该设备可以接收数据 我已经使用 C 应用程序对其进行了测试 效果很好 另外 我可以使用 pywinusb 从设备读取数据 但我在尝试发送数据时遇到问题 这是我正在运行
  • 如何在 ADB 连接期间禁用电池充电?

    问题描述 每次我在电脑和手机之间连接 USB 线时 电池都会自动充电 我想使用 ADB 协议 但我不想在 ADB 连接期间为电池充电 是否可以关闭此充电功能 当然 我该怎么做呢 环境 Android 操作系统 4 及更高版本的手机 我只需要
  • ASP.NET/Silverlight 控制 USB 设备

    我想使用某种 USB 设备 例如闪光灯 提醒用户有新消息 是否可以从 ASP NET 或 Silverlight 控制 USB 设备 您可以在网页中使用自定义 ActiveX 组件和一些 javascript 来完成此操作 或者 您可以使用
  • Android 10 中没有设备筛选器的 USB_DEVICE_ATTACHED

    我正在开发一个 Android 应用程序 它在清单中为 BroadcastReceiver 注册了四个意图过滤器 这些都是 android hardware usb action USB DEVICE ATTACHED android ha
  • Swift 上的 USB 连接委托

    Swift 中是否有一个代表可以让我的班级知道何时通过计算机的 USB 插入新设备 我想知道我的程序何时可以使用新设备 Eric Aya 的答案已经相当不错了 但这里有一个 Swift 3 的改编 我把大部分丑陋的东西包裹在一个USBWat

随机推荐

  • vue3父组件给子组件传值

    父组件
  • Could not acquire change log lock. Currently locked by XXXXXX

    最近公司项目使用到了flowable 为了方便业务开发人员使用流程设计器画流程图 使用了flowable的原生流程设计器modeler 用docker部署非常方便 参考flowable流程设计器部署官网文档 用docker部署flowabl
  • JAVA 10

    重写 一 重写是父类与子类之间多态的一种表现 二 重写方法的参数列表必须与被重写的方法完全相同 三 重写方法的访问符一定要大于被重写方法的访问修饰符 要保证public gt protected gt default gt private
  • Node-包管理工具整套下载使用讲解(nvm、npm、yarn、cnpm、pnpm、nrm)

    前言 包管理工具npm Node Package Manager 就是Node包管理器 现在已经不仅仅是node的包管理器了 我们前端项目也都会用它来进行管理项目依赖的包 如何下载和安装npm工具 npm属于node的一个管理工具 所以我们
  • 管螺纹如何标注_螺纹画法图解,你了解多少?

    螺纹是机械产品中常见的联接件 在绘制机械图纸时 螺纹的画法是有讲究的 国家机械制图标准中对螺纹画法做出了详细的规定 不同螺纹画法也不尽相同 螺纹的画法图解 01 外螺纹 外螺纹不论其牙形如何 螺纹的牙顶 大径 及螺纹终止线用粗实线表示 螺杆
  • merge into 的用法

    1 语法 merge into 的语法如下所示 MERGE INTO target table T USING source table sql S ON conditional expression and WHEN MATCHED TH
  • apache-Tomcat 8.5.39安装包 安装过程以及启动文件详解

    1 apache tomcat8 5 39 安装包地址 链接 https pan baidu com s 1 EIftvG A XM8VEfkDlJLQ 提取码 fxpp 复制这段内容后打开百度网盘手机App 操作更方便哦 说明 tomca
  • MQTT Paho + Mosquitto 快速搭建 & 双向认证

    今天继续聊SOA的话题 前面聊过了SOME IP和DDS 很多文章提到SOA协议 还会把HTTP和MQTT也放进来讨论 但个人认为 目前只有SOME IP和DDS适合作为车载SOA的中间件方案 而HTTP和MQTT则更适合应用于车联网云端交
  • tf并行计算学习

    Tensorflow并行计算 Liu91 2018 08 13 14 09 19 字数 1 294 阅读 8 724 在真正开始Tensorflow并行运算代码实现之前 我们首先了解一下Tensorflow系统结构设计是如何完美的支持并行运
  • JDBC连接MySQL并且查询操作。

    1 package com imooc db 2 3 4 import java sql Connection 5 import java sql DriverManager 6 import java sql ResultSet 7 im
  • 路由器NAT功能配置简介

    路由器NAT功能配置简介 随着internet的网络迅速发展 IP地址短缺已成为一个十分突出的问题 为了解决这个问题 出现了多种解决方案 下面几绍一种在目前网络环境中比较有效的方法即地址转换 NAT 功能 一 NAT简介 NAT Netwo
  • 七夕将至,来看看程序员的浪漫吧

    七夕快要到了 下面来看几个玫瑰 选一个合适的送给心仪的 ta 把 第一束 index页面 div div div div div div
  • 芯片之家精选文章合集 (二):收藏起来慢慢看

    文 晓宇 上一次文章合集是2019 02 24 2020 03 28期间的 芯片之家精选文章合集 一 收藏起来慢慢看 点击阅读 本次 我们重新整理下过去一年写的文章 本次的文章合集是2020 03 29 2021 01 31期间的 文章内容
  • JavaScript 中const的使用

    ES2015 引入了两个重要的 JavaScript 新关键词 let 和 const 通过 const 定义的变量与 let 变量使用方法类似 let的使用点击下面链接 JavaScript 中let的使用 安之若素 的博客 CSDN博客
  • 最新总结的软件测试宝典,花2天时间阅完,软件测试面试就过了......

    1 测试人员需要何时参加需求分析 如果条件循序 原则上来说 是越早介入需求分析越好 因为测试人员对需求理解越深刻 对测试工作的开展越有利 可以尽早的确定测试思路 减少与开发人员的交互 减少对需求理解上的偏差 2 软件测试与调试的关系 测试条
  • 证书登录失败_爱思助手IPA签名证书申请失败的解决方案

    本文以下解决方法仅供参考 你也可以找朋友给你签一个 实在不行还可以换个工具 毕竟简单的解决问题才是主要目标 比较常见的问题有 证书申请失败 SendAuthenticationRequest em node Your Apple ID or
  • vue 如何实现在函数中触发路由跳转

  • Hql

    1 查询整个映射对象所有字段 直接from查询出来的是一个映射对象 即 查询整个映射对象所有字段 String hql from Users Query query session createQuery hql List
  • Java反编译工具(以反编译android的framework.jar举例)

    framework jar包含android框架层的代码 如果我们在framework层添加了代码 如何确定我们的代码是否真的被编译进入framework jar当中呢 很简单 反编译就好了 下面将介绍2款反编译工具 有一点需要注意 它们都
  • Multi-threaded applications and asynchronous I/O(翻译)

    此文章使用Goolge进行翻译学习使用 原文网址为 http libusb sourceforge net api 1 0 mtasync html 本文章是为了调查 libusb bulk transfer 会阻塞 阻塞时间长达60s的问