首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

从多线程调用uv_write,它的回调永远不会被调用

uv_write 是 libuv 库中的一个函数,用于异步地将数据写入到一个流(stream)中。libuv 是一个跨平台的异步 I/O 库,常用于 Node.js 的底层实现。当从多线程环境调用 uv_write 时,可能会遇到回调函数永远不会被调用的问题。以下是关于这个问题的基础概念、原因分析以及解决方案。

基础概念

  • libuv: 一个支持跨平台的异步 I/O 库,提供了事件循环、异步 TCP/UDP、文件系统操作等功能。
  • uv_write: libuv 中的一个函数,用于异步写入数据到流。
  • 回调函数: 在异步操作完成后,用于处理结果的函数。

原因分析

  1. 线程安全问题: libuv 的事件循环(event loop)通常在一个单独的线程中运行。如果在其他线程中直接调用 uv_write,可能会导致竞态条件,使得事件循环无法正确处理写入操作。
  2. 事件循环阻塞: 如果事件循环被阻塞,它将无法处理任何异步操作,包括 uv_write 的回调。
  3. 资源泄漏: 如果在多线程环境中不正确地管理资源,可能会导致内存泄漏或其他资源问题,从而影响回调的执行。

解决方案

1. 使用 uv_queue_work

uv_queue_work 可以将工作项排队到事件循环线程中执行,从而避免直接在多线程环境中调用 uv_write

代码语言:txt
复制
void write_work(uv_work_t *req) {
    uv_stream_t *stream = req->data;
    uv_buf_t buf;
    buf.base = "hello";
    buf.len = 5;
    uv_write((uv_write_t *)req, stream, &buf, 1, write_done);
}

void write_done(uv_write_t *req, int status) {
    if (status < 0) {
        fprintf(stderr, "Write error %s\n", uv_strerror(status));
    }
    free(req);
}

void async_write(uv_stream_t *stream) {
    uv_work_t *req = (uv_work_t *)malloc(sizeof(uv_work_t));
    req->data = stream;
    uv_queue_work(uv_default_loop(), req, write_work, write_done);
}

2. 使用线程池

libuv 提供了一个线程池,可以通过 uv_threadpool_queue_work 将任务排队到线程池中执行。

代码语言:txt
复制
void write_work(uv_work_t *req) {
    uv_stream_t *stream = req->data;
    uv_buf_t buf;
    buf.base = "hello";
    buf.len = 5;
    uv_write((uv_write_t *)req, stream, &buf, 1, write_done);
}

void write_done(uv_write_t *req, int status) {
    if (status < 0) {
        fprintf(stderr, "Write error %s\n", uv_strerror(status));
    }
    free(req);
}

void async_write(uv_stream_t *stream) {
    uv_work_t *req = (uv_work_t *)malloc(sizeof(uv_work_t));
    req->data = stream;
    uv_threadpool_queue_work(uv_default_loop(), req, write_work, write_done);
}

3. 确保事件循环不被阻塞

确保事件循环线程不被长时间运行的任务阻塞。可以使用 uv_async_send 来通知事件循环处理某些任务。

代码语言:txt
复制
uv_async_t async;

void async_cb(uv_async_t *handle) {
    // 处理异步任务
}

void init_async() {
    uv_async_init(uv_default_loop(), &async, async_cb);
}

void trigger_async() {
    uv_async_send(&async);
}

应用场景

  • 高并发服务器: 在处理大量并发连接时,使用 libuv 可以有效地管理 I/O 操作。
  • 跨平台应用: libuv 的跨平台特性使其适用于需要在不同操作系统上运行的应用程序。

通过上述方法,可以解决从多线程调用 uv_write 导致回调函数不被调用的问题,并确保异步 I/O 操作的正确性和效率。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

libuv源码分析之stream第一篇

接着执行用户的回调。 4 连接一个流,比如作为客户端去连接服务器。就是io观察者中的文件描述符。可读事件触发时(建立三次握手成功),执行用户的回调。 5 监听一个流,就是io观察者中的文件描述符。...并且给一些字段赋值,比如读回调函数,分配内存的函数。打上正在做读取操作的标记。然后在可读事件触发的时候,读回调就会被执行,这个逻辑我们后面分析。除了开始读取数据,还有一个读操作就是停止读取。...cb ) { return uv_write2(req, handle, bufs, nbufs, NULL, cb); } uv_write是直接调用uv_write2。...代表是一般的写数据,不传递文件描述符。...write_queue_size == 0); // 初始化一个写请求 uv__req_init(stream->loop, req, UV_WRITE); // 写完后执行的回调 req

91920
  • 你不知道的JavaScript(中卷)二

    这些回调中的做任意一个都无法影响或延误对其他回调的调用 • Promise调度技巧:永远都不应该依赖于不同Promise间回调的顺序和调度。...实际上,好的编码实践方案根本不会让多个回调的顺序有丝毫影响,可能的话就要避免 3.回调未调用:没有任何东西(甚至JS错误)能阻止Promise向你通知它的决议(如果它决议了的话)。...• 任何通过then()注册的(每个)回调只会被调用一次,如果把同一个回调注册了不止一次,那它被调用的次数就会和注册次数相同。...但不管这个值是什么,无论当前或未来,它都会传给所有注册的(且适当的完成或拒绝)回调 • 如果使用多个参数调用resovle()或者reject(),第一个参数之后的所有参数都会被默默忽略。...Promise,我们可以将其链接起来 • 不管从then()调用的完成回调(第一个参数)返回的值是什么,它都会被自动设置为被链接Promise(第一点中的)的完成 • 如果你调用

    80020

    JavaScript中的Event Loop机制详解(前端必看)

    下面这个图片非常直观的展示了这个过程,其中的global就是初次运行脚本时向执行栈中加入的代码:  从图片可知,一个方法执行会向执行栈中加入这个方法的执行环境,在这个执行环境中还可以调用其他方法,甚至是自己...这两者的顺序是不固定的,收到代码运行的环境的影响。...close阶段 当一个socket连接或者一个handle被突然关闭时(例如调用了socket.destroy()方法),close事件会被发送到这个阶段执行回调。...node会在可以执行timer回调的第一时间去执行你所设定的任务。 setImmediate() 方法从意义上将是立刻执行的意思,但是实际上它却是在一个固定的阶段才会执行回调,即poll阶段之后。...: immediate timeout 因为在I/O事件的回调中,setImmediate方法的回调永远在timer的回调前执行。

    59920

    浏览器和Node.js的EventLoop事件循环机制知多少?

    调用栈(Call Stack) 调用堆栈:负责追踪所有要执行的代码。每当调用堆栈中的函数执行完毕时,就会从栈中弹出此函数,如果有代码需要输入就会执行PUSH操作。...上图是Node.js的EventLoop流程图,我们依次进行分析得到: Timers阶段:执行的是setTimeout和setInterval I/O回调阶段:执行系统级别的回调函数,比如TCP执行失败的回调函数...Check阶段:setImmediate() 回调函数在这里执行。 Close回调阶段:一些关闭的回调函数,如:socket.on('close', ...)。...如果任何时刻在给定的阶段调用Process.nextick,则所有被传入Process.nextTick的回调,将会在事件循环继续往下执行前被执行,这可能导致事件循环永远无法到达轮询阶段。...为了避免浏览器一直处于繁忙的状态,导致requestIdlecallback函数永远无法执行回调,浏览器提供了一个额外的setTimeout函数,为这个任务设置截止时间,浏览器就可以根据这个截止时间规划这个任务的执行

    1.7K20

    Python 的异步 IO:Asyncio 简介

    Asyncio 并不能带来真正的并行(parallelism)。当然,因为 GIL(全局解释器锁)的存在,Python 的多线程也不能带来真正的并行。...run_until_complete 是一个阻塞(blocking)调用,直到协程运行结束,它才返回。这一点从函数名不难看出。...所以,我们可以写得更明显一些: 完整代码: 运行结果: 回调 假如协程是一个 IO 的读操作,等它读完数据后,我们希望得到通知,以便下一步数据的处理。...run_forever 会一直运行,直到 stop 被调用,但是你不能像下面这样调 stop: run_forever 不返回,stop 永远也不会被调用。...要解决这个问题,可以用 gather 把多个协程合并成一个 future,并添加回调,然后在回调里再去停止 loop。

    1.3K80

    重新认识javascript的settimeout和异步

    那里永远为真,造成死循环。...2、ajax请求回调 接着我们来测试一下通过xmlhttprequest实现ajax异步请求调用,主要代码如下: var xmlReq = createXMLHTTP();//创建一个xmlhttprequest...message\":\"" + action + "\"}"; context.Response.Write(jsonObject); } 理论上,如果ajax异步请求,它的异步回调函数是在单独一个线程中...,那么回调函数必然不被其他线程”阻挠“而顺利执行,也就是1秒后,它回调执行弹出‘ajax’,可是实际情况并非如此,回调函数无法执行,因为浏览器再次因为死循环假死。...结论:根据实践结果,可以得出,javascript引擎确实是单线程处理它的任务队列(能理解成就是普通函数和回调函数构成的队列吗?)的。

    98390

    Node中的事件循环和异步API

    Node在两者之间给出了它的解决方案:利用单线程,远离多线程死锁、状态同步等问题;利用异步I/O,让单线程远离阻塞,以好使用CPU。...当主线程发起I/O调用时,I/O操作会被放在I/O线程来执行,主线程继续执行下面的任务,在I/O线程完成操作后会带着数据通知主线程发起回调。...1.3 请求对象 对于Node中的异步I/O调用而言,回调函数不由开发者来调用,从JS发起调用到I/O操作完成,存在一个中间产物,叫请求对象。...使用它们创建的定时器会被放入timers队列的一个红黑树中,每次事件循环执行时会从相应队列中取出并判断是否超过定时时间,超过就形成一个事件,回调立即执行。...它使用libuv的API来设定在 poll 阶段结束后立即执行回调。

    1.6K30

    《深入浅出Node.js》:Node异步编程解决方案 之 事件发布-订阅模式

    虽然性能提升可以用多线程方式解决,但多线程的引入对业务逻辑造成的麻烦也不小。Node利用异步非阻塞I/O并通过事件循环触发异步回调的机制,将异步提升到业务层面,已被证明是一种新的有效的性能提升思路。...异步编程的难点在于几点:异常处理、函数嵌套过深、阻塞代码、多线程编程、异步转同步。(这里我不展开描述,因为我的理解也不深,后期有理解再写吧。...这里面事件的设计非常重要,它关乎外部调用组件时是否优雅,从某种角度来说事件的设计就是组件的接口设计。...这里就是利用once()方法将所有请求都压入事件队列中,利用其执行一次就会移除监听器的特点,保证每一个监听器(回调函数)只会被执行一次。...对于相同的SQL语句,保证在同一个查询开始到结束的过程中永远只有一次。SQL在进行查询时,新到来的相同调用只需在队列中等待数据即可,一旦查询结束,得到的结果可以被这些调用共同使用。

    1.3K30

    JavaScript异步编程设计快速响应的网络应用

    JavaScript代码永远不会被中断,这是因为代码在运行期间内只需要安排队事件即可,而这些事件在代码运行结束之前不会被触发! 请参考:JavaScript事件驱动机制&定时器机制 2....异步函数的编写 调用一个函数(异步函数)时,程序只在该函数返回之后才能继续。这个函数会到导致将来再运行另一个函数(回调函数)。...有些函数既返回有用的值,又要取用回调。这种情况下,切记回调有可能被同步调用(返值之前),也有可能被异步调用(返值之后)。 永远不要定义一个潜在同步而返值却有可能用于回调的函数(回调依赖返回值)。...所以,只能在回调内部处理源于回调的异步错误。...嵌套式回调的解嵌套 JavaScript中最常见的反模式做法是,回调内部再嵌套回调。 请避免两层以上的函数嵌套。

    2.1K31

    《深入浅出Node.js》-异步IO

    Node 的异步 I/O 事件循环 事件循环是 Node 自身的执行模型,正是它使得回调函数十分普遍。...请求对象 对于 Node 中的异步 I/O 而言,回调函数究竟是谁在调用呢?比如下述代码,当文件打开成功后,后面的回调的执行过程是怎样的呢?...从 JavaScript 层传入的参数和当前方法都封装在这个请求对象中,回调函数也是这个请求对象的一个属性。而操作系统拿到这个对象后,将 FSReqWrap 对象推入线程池中等待执行。...调用 setTimeout/setInterval 创建的定时器会被插入定时器观察者内部的红黑树中,每次 Tick 执行时,会从该红黑树中迭代选出定时器对象,检查是否超过时间,如果超过,它的回调函数立即执行...执行回调函数的是定时器观察者。 定时器的问题在于,它并非精确的,尽管事件循环非常快,但是如果每一次循环占用时间较多,那么下次循环时,它可能已经超时很久了。

    74230

    并发服务器(三):事件驱动

    出现了这种情况后,我们调用一个叫做 的回调函数,传入相应的文件描述符。这个调用意味着客户端连接到套接字上,发送某些数据,并且对套接字上 的调用不会被阻塞注6。这个回调函数返回结构体 。...对于主循环中某个准备好了写入数据的描述符,代码是类似的,除了它所调用的回调函数,这个回调函数叫做 。 现在来花点时间看看这个回调: 是全状态对象,用来表示在主循环中两次回调函数调用之间的客户端的连接。...因为回调函数在客户端发送的某些数据时被调用,不能假设它能够不停地与客户端通信,并且它得运行得很快,不能被阻塞。因为套接字被设置成非阻塞模式, 会快速的返回。...同步、异步、事件驱动、回调 示例代码为讨论什么是异步编程、它和事件驱动及基于回调的编程有何联系,提供了一个良好的背景。因为这些词汇在并发服务器的(非常矛盾的)讨论中很常见。...注意这里的 函数是回调函数;它们永远不会阻塞,并且只有网络事件触发的时候才会被调用。它们可以获得部分数据,并能够在调用过程中保持稳定的状态。

    1.6K50

    在chromev8中的JavaScript事件循环分析

    每一个消息都关联着一个用以处理这个消息的回调函数。 在事件循环期间的某个时刻,运行时会从最先进入队列的消息开始处理队列中的消息。被处理的消息会被移出队列,并作为输入参数来调用与之关联的函数。...我们可以通过使用 Loupe(Loupe是一种可视化工具,可以帮助您了解JavaScript的调用堆栈/事件循环/回调队列如何相互影响)工具来了解上面代码的执行情况。...同一次事件循环中,微任务永远在宏任务之前执行。...microtask队列,最后执行 console.log('end'),输出end 调用栈中的代码执行完成(全局代码属于宏任务),接下来开始执行微任务队列中的代码,执行promise回调,输出promise1...的回调,输出setTimeout 最后的执行结果如下 [执行结果.png] 总结 js的异步的实现有赖于事件循环的支撑,而在浏览器不崩溃的前提下,通过执行栈与事件队列在宏任务与微任务中左右横跳,从而令浏览器事件不形成死锁

    4K40

    当面试官问你Promise的时候,他究竟想听到什么?

    传统的回调式异步操作有什么缺点 (Promise如何解决异步信任问题的) 传统的回调有五大信任问题: 调用回调太早 调用回调过晚(或没有被调用) 调用回调次数过少或过多 未能传递所需的环境和参数 吞掉可能出现的错误和异常...,提供给then的回调也总会被异步调用。...而Promise对象调用resolve()和reject()时,每个注册的观察回调也都会被自动调度。所以这些观察回调的任意一个都无法影响或延误对其他回调的调用。 此外,关于回调未调用。...如果Promise永远不被决议的话,Promise本身已提供了竞态的抽象机制来作为解决方案。 3.调用回调次数过少或过多 Promise的定义方式使得它只能被决议一次。...即使代码中出现多次决议,这个Promise也会接受第一次决议,并会忽略掉其他任何后续调用。所以任何通过then()注册的回调只会被调用一次。

    2.7K50

    Rx.NET 简介

    在另一端, 一旦管道上有了新的值, 那么管道的观察者就会得到通知, 这些观察者通过提供回调函数的方式来注册到该管道上. 管道每次更新的时候, 这些回调函数就会被调用, 从而刷新了观察者的数据....这个例子里, Observable就是管道, 一系列的值在这里被生成. Observer(观察者)在Observable有新的值的时候会被通知....序列 Observable.Never 返回一个没有值, 且永远不会结束的序列 Observable.Throw(exception), 返回一个带有错误的序列 Observable.Return(xxx...异步和多线程 异步就表示不一定按顺序执行, 但是它可以保证非阻塞, 通常会有回调函数(或者委托或者async await). 但是异步对于Rx来说就是它的本性 Rx的同步异步对比: ?...多线程 Rx不是多线程的, 但是它是线程自由的(就是可以使用多个线程), 它被设计成只是用必须的线程而已. 多线程表示, 同时有多个线程在执行. 也可以称作并发. 它可以分担计算量.

    3.6K90

    关于js中的回调函数callback

    这个时间段作为函数的第二个参数被传入。如果队列中没有其它消息,消息会被马上处理。但是,如果有其它消息,setTimeout 消息必须等待其它消息处理完。...点击事件的回调函数 ? 数组中遍历每一项调用的回调函数 ?...同步回调的例子 所以回调与同步、异步并没有直接的联系,回调只是一种实现方式,既可以有同步回调,也可以有异步回调,还可以有事件处理回调和延迟函数回调,这些在我们工作中有很多的使用场景 所以其实并不是我们不认识回调函数...,所以js在同步机制的缺陷下设计出了异步模式 在异步执行的模式下,每一个异步的任务都有其自己一个或着多个回调函数,这样当前在执行的异步任务执行完之后,不会马上执行事件队列中的下一项任务,而是执行它的回调函数...完结 以上就是本篇文章的全部内容,由对回调函数的陌生到熟悉和使用,以及对同步/异步的概念,还有js的执行机制以及浏览器内核的多线程机制相信大家都有了一个简单的知识脉络,希望通过此文提到的内容,每个小伙伴去查阅更深入的资料

    5.6K50
    领券