You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用Boost协程的短程序在Windows上为何总是崩溃?

问题:Windows MSYS2环境下Boost ASIO协程程序崩溃/挂起

在Windows MSYS2环境使用Boost 1.89.0-1.91.0版本时,以下程序无法正常终止,总是出现挂起或崩溃情况,但在Linux的Goldbot在线编译器中可正常运行,推测和ASIO在Windows上的协程实现有关。程序使用MSYS包中的GCC 15.2.0和16.1.0编译。

#include <boost/asio.hpp>
#include <iostream>

using namespace boost::asio;

int main() {
    for (int i = 0; i < 5; i++) {
        io_context ioc;
        co_spawn(
            ioc,
            [i]() -> boost::asio::awaitable<void> {
                auto ex = co_await this_coro::executor;
                steady_timer timer(ex, std::chrono::milliseconds(100));
                co_await timer.async_wait(use_awaitable);
                std::cout << "Timer expired # " << i << '\n';
            },
            detached);
        ioc.run();
    }
    return 0;
}

GDB崩溃输出

$ gdb ./program.exe
GNU gdb (GDB) 17.1
Copyright (C) 2025 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-w64-mingw32".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./program.exe...
(gdb) run
Starting program: C:\msys64\home\me\program.exe
[New Thread 9256.0xa328]
[New Thread 9256.0x54c]
[New Thread 9256.0x2ce0]
[New Thread 9256.0x946c]
Timer expired # 0

Thread 5 received signal SIGSEGV, Segmentation fault.
[Switching to Thread 9256.0x946c]
0x00007ff6aaad04de in _InterlockedExchange (Target=0xfeeefeeefeeeff4e, Value=1) at C:/msys64/mingw64/include/psdk_inc/intrin-impl.h:1738
1738        return __sync_lock_test_and_set(Target, Value);
(gdb)

额外调试信息

(gdb) info threads
  Id   Target Id          Frame
  1    Thread 9256.0xb2d4 0x00007ffc9bb43414 in ntdll!ZwCreateThreadEx () from C:\WINDOWS\SYSTEM32\ntdll.dll
  2    Thread 9256.0xa328 0x00007ffc9bb457b4 in ntdll!ZwWaitForWorkViaWorkerFactory () from C:\WINDOWS\SYSTEM32\ntdll.dll
  3    Thread 9256.0x54c  0x00007ffc9bb457b4 in ntdll!ZwWaitForWorkViaWorkerFactory () from C:\WINDOWS\SYSTEM32\ntdll.dll
  4    Thread 9256.0x2ce0 0x00007ffc9bb457b4 in ntdll!ZwWaitForWorkViaWorkerFactory () from C:\WINDOWS\SYSTEM32\ntdll.dll
* 5    Thread 9256.0x946c 0x00007ff6aaad04de in _InterlockedExchange (Target=0xfeeefeeefeeeff4e, Value=1) at C:/msys64/mingw64/include/psdk_inc/intrin-impl.h:1738
(gdb) bt
#0  0x00007ff6aaad04de in _InterlockedExchange (Target=0xfeeefeeefeeeff4e, Value=1) at C:/msys64/mingw64/include/psdk_inc/intrin-impl.h:1738
#1  boost::asio::detail::win_iocp_io_context::timer_thread_function::operator() (this=0x260d8c0)
    at C:/msys64/home/me/program/build/debug/_deps/boost-src/libs/asio/include/boost/asio/detail/impl/win_iocp_io_context.ipp:73
#2  boost::asio::detail::posix_thread::func<boost::asio::detail::win_iocp_io_context::timer_thread_function, std::allocator<void> >::run (this=0x260d8b0)
    at C:/msys64/home/me/program/build/debug/_deps/boost-src/libs/asio/include/boost/asio/detail/posix_thread.hpp:119
#3  0x00007ff6aac1a10a in boost::asio::detail::asio_detail_posix_thread_function (arg=<optimized out>)
    at C:/msys64/home/me/program/build/debug/_deps/boost-src/libs/asio/include/boost/asio/detail/impl/posix_thread.ipp:75
#4  0x00007ff6aa9f68dc in pthread_create_wrapper ()
#5  0x00007ffc9b92f0ad in msvcrt!_beginthreadex () from C:\WINDOWS\System32\msvcrt.dll
#6  0x00007ffc9b92f17c in msvcrt!_endthreadex () from C:\WINDOWS\System32\msvcrt.dll
#7  0x00007ffc9b6fe8d7 in KERNEL32!BaseThreadInitThunk () from C:\WINDOWS\System32\kernel32.dll
#8  0x00007ffc9ba6c3fc in ntdll!RtlUserThreadStart () from C:\WINDOWS\SYSTEM32\ntdll.dll
#9  0x0000000000000000 in ?? ()

编辑:将io_context ioc设为static,并在每次ioc.run()后调用ioc.restart()可解决崩溃问题,推测是io_context销毁与Windows定时器线程终止之间存在竞态条件导致。


问题分析与解决方案

根本原因

从调试栈可以看出,崩溃发生在Boost ASIO的Windows IOCP定时器线程中,访问了已释放的内存地址0xfeeefeeefeeeff4e(这是Windows标记已释放堆内存的典型值)。这说明**io_context对象在其内部定时器线程完全终止前就被销毁**,导致线程后续执行时访问了已被释放的内存。

在Windows平台下,Boost ASIO的win_iocp_io_context会创建专用的定时器线程来处理定时任务,即使io_context.run()返回(表示所有异步任务已完成),定时器线程可能还未完全退出。当循环中每次创建的io_context对象在迭代结束时被销毁,此时定时器线程可能仍在运行,就会触发非法内存访问。

而Linux平台下ASIO的定时器实现机制不同,不存在专用定时器线程,因此不会出现该竞态条件。

解决方案

除了你发现的将io_context设为static并调用restart()的方法外,还有两种更规范的处理方式:

  1. 循环外复用io_context
    io_context放在循环外部,每次迭代前调用restart()重置状态,这样可以避免频繁创建销毁io_context,从根源上避免竞态条件:

    #include <boost/asio.hpp>
    #include <iostream>
    
    using namespace boost::asio;
    
    int main() {
        io_context ioc; // 循环外创建,复用io_context
        for (int i = 0; i < 5; i++) {
            ioc.restart(); // 每次迭代前重置
            co_spawn(
                ioc,
                [i]() -> boost::asio::awaitable<void> {
                    auto ex = co_await this_coro::executor;
                    steady_timer timer(ex, std::chrono::milliseconds(100));
                    co_await timer.async_wait(use_awaitable);
                    std::cout << "Timer expired # " << i << '\n';
                },
                detached);
            ioc.run();
        }
        return 0;
    }
    
  2. 显式等待线程终止(不推荐)
    ASIO没有直接暴露等待内部线程的接口,需要通过额外手段追踪线程状态并等待,实现复杂且依赖内部实现细节,因此不推荐使用。

额外说明

该问题属于Boost ASIO在Windows IOCP实现下的特定竞态条件,目前在1.89-1.91版本中存在。如果需要保持循环内创建io_context的结构,短暂延迟销毁可能缓解问题,但这依赖系统调度,可靠性低,最稳妥的方式还是复用io_context

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

火山引擎 最新活动