You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免用户自定义Autoit回调函数的并发调用?

问题描述

我正在修改Boost代码以适配AutoIt,原项目为WebSocketAsioAHK,我的版本分支是dp_update。当前需要解决的核心问题是:如何防止用户提供的AutoIt回调函数被并发调用。

现有on_read回调代码如下:

/// Callback registered by async_read. It calls user registered callback to actually process the data. And then issue another async_read to wait for data from server again.
/// \param ec instance of error code
/// \param bytes_transferred
void
on_read(
        beast::error_code ec,
        std::size_t bytes_transferred) {
    if(EnableVerbose)
    {
        boost::lock_guard<boost::mutex> guard(mtx_);
        std::wcout << L"<WsDll-" ARCH_LABEL "> in on read" << std::endl;
    }       
    boost::ignore_unused(bytes_transferred);

    {
        boost::lock_guard<boost::mutex> guard(mtx_);
        if(!Is_Connected) {
            return;
        }

    }

    // error occurs
    if (ec) {
        if(on_fail_cb)
            on_fail_cb(L"read");
        return fail(ec, L"read");
    }

    const std::string data = beast::buffers_to_string(buffer_.data());
    const std::wstring wdata(data.begin(), data.end());
    if(EnableVerbose)
    {
        boost::lock_guard<boost::mutex> guard(mtx_);
        std::wcout << L"<WsDll-" ARCH_LABEL "> received[" << bytes_transferred << L"] " << wdata << std::endl;
    }       

//  The next section is where my issue resides

    if (on_data_cb)
        on_data_cb(wdata.c_str(), wdata.length());

    buffer_.consume(buffer_.size());

    if(EnableVerbose)
    {
        boost::lock_guard<boost::mutex> guard(mtx_);
        std::wcout << L"<WsDll-" ARCH_LABEL "> issue new async_read in on_read" << std::endl;
    }       
    ws_.async_read(
            buffer_,
            beast::bind_front_handler(
                    &session::on_read,
                    shared_from_this()));

    // Close the WebSocket connection
    // ws_.async_close(websocket::close_code::normal,
    //     beast::bind_front_handler(
    //         &session::on_close,
    //         shared_from_this()));
}

其中if (on_data_cb) on_data_cb(wdata.c_str(), wdata.length());是触发AutoIt回调的代码,需要保证这段代码不会被同时执行多次。


解决方案

针对这个问题,提供两种适配现有代码的可行方案,可根据回调执行耗时和性能需求选择:

方案一:互斥锁直接保护回调调用

利用你已有的mtx_互斥量,将回调调用逻辑包裹在锁范围内,强制同一时刻只有一个线程能执行回调:

// 替换原有的回调调用代码
if (on_data_cb) {
    boost::lock_guard<boost::mutex> guard(mtx_);
    on_data_cb(wdata.c_str(), wdata.length());
}

注意:如果AutoIt回调执行耗时较长,会阻塞后续async_read的触发,可能导致WebSocket消息接收延迟或积压,这种情况下更推荐方案二。

方案二:串行任务队列异步执行回调

借助Boost Asio的strand(串行执行器),将回调任务投递到串行队列中执行,既避免并发调用,又不会阻塞WebSocket的消息接收流程:

  1. 首先在session类中添加strand成员变量:
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
  1. 在session构造函数中初始化strand:
session(boost::asio::io_context& ioc)
    : ws_(ioc), strand_(boost::asio::make_strand(ioc))
{
    // 保留原有初始化逻辑
}
  1. 修改回调调用代码,将任务投递到strand中:
if (on_data_cb) {
    // 复制数据避免生命周期失效问题
    std::wstring data_copy = wdata;
    boost::asio::post(strand_, [this, data_copy]() {
        if (on_data_cb) {
            on_data_cb(data_copy.c_str(), data_copy.length());
        }
    });
}

此方案中,strand会保证所有回调任务按顺序串行执行,不会出现并发调用,同时不会阻塞async_read的发起,不影响WebSocket的接收性能。


额外注意事项

  • AutoIt回调函数本身大概率不是线程安全的,无论采用哪种方案,都要确保回调执行期间,AutoIt端不会有其他线程操作共享资源。
  • 若选择方案一,务必评估回调的执行耗时,避免因长时间锁阻塞导致WebSocket连接异常。

内容的提问来源于stack exchange,提问作者Dan Pollak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 03:15:28