我有一个用C++为Windows编写的程序,这是一个浏览器扩展的本地应用程序.基本上,它通过stdin从浏览器接收消息,并通过stdout发回响应.消息是需要使用外部应用程序(如curl)下载的URL,响应是这些下载的进度/完成.

我的计划流程如下:

1-有一个不断读取stdin条消息并从浏览器接收消息的主循环

2-主循环为每条消息创建一个std::thread.向线程提供要下载的URL并启动,然后主循环返回到侦听新消息.

3-在线程中,我使用CreateProcess()产生一个子进程,比方说curl.exe,并继续读取它的输出.

4-现在这些线程需要将下载进度发送到浏览器,这是它们通过向程序stdout写入来完成的.因为多个线程需要同时向它写入数据,所以我有一个用std::lock_guard<std::mutex>保护的函数,线程使用这个函数写入stdout.

现在我想把这个程序移植到Linux上,我希望简单地用popen()取代CreateProcess(),但我在谷歌上搜索了一下它是否线程安全,尽管我找不到一个明确的答案,但大多数答案都表明它不是.显然,它在引擎盖下使用fork()forks and threads don't get along well.

看起来Linux的方法是设置为fork(),然后使用管道在主程序和分支程序之间进行通信,但这需要我更改程序的整个 struct ,因为它当前是基于线程的.此外,我不知道如何在维护主循环的同时读取来自所有这些Forking 的子进程的管道.我无法想象在不使用线程的情况下这样做,这会让我们回到第一个问题.

所以我想知道有没有其他方法可以做到这一点?

以下是该程序运行方式的simplified个版本:

std::mutex theMutex;

int main()
{
    while(true)
    {
        char* url = new char[message_length];
        fread(url, sizeof(char), message_length, stdin);
        
        std::thread th1(download_thread, url);
        th1.detach();
    }
    
    return 0;
}

void download_thread(const string url)
{
    /* create the process */
    CreateProcessW(
            "curl.exe",
            "curl.exe url",
            NULL,
            NULL,
            TRUE,
            processFlags,
            NULL,
            NULL,
            &siStartInfo,
            &piProcInfo);
    
    /* keep reading the output of the process until it exits*/
    const int BUFSIZE = 1024;
    char buf[BUFSIZE];
    unsigned long bytesRead = 0;
    bSuccess = FALSE;

    while(true)
    {
        bSuccess = ReadFile(h_child_stdout_r, buf, BUFSIZE, &bytesRead, NULL);

        if(!bSuccess || bytesRead<=0)
        {
            break;
        }
        
        string output(buf, buf+bytesRead);
        write_to_stdout(msg);
    }
}

void write_to_stdout(const string msg)
{
    std::lock_guard<std::mutex> lock(theMutex);
    
    const unsigned int message_length = msg.length();
    fwrite(msg.c_str(), sizeof(char), message_length, stdout);
    fflush(stdout);
}

推荐答案

[res.on.objects]
1 The behavior of a program is undefined if calls to standard library functions from different threads may introduce a data race. The conditions under which this may occur are specified in [res.on.data.races]

[res.on.data.races]
2 A C++ standard library function shall not directly or indirectly access objects ([intro.multithread]) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function's arguments, including this.
3 A C++ standard library function shall not directly or indirectly modify objects ([intro.multithread]) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function's non-const arguments, including this.

因此,popen不可能引发一场数据竞赛.

$ man popen
  ...
ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).
   ┌────────────────────────────────────────────────────┬───────────────┬─────────┐
   │Interface                                           │ Attribute     │ Value   │
   ├────────────────────────────────────────────────────┼───────────────┼─────────┤
   │popen(), pclose()                                   │ Thread safety │ MT-Safe │
   └────────────────────────────────────────────────────┴───────────────┴─────────┘
   

Linux相关问答推荐

为什么在Linux上STD::SLEEP_FOR(STD::Chrono::Hors::Max())会立即返回?

我需要从 Ubuntu 中删除 .txt 文件中以白色间距分隔的行的白色间距

替换前 3 个字符范围内的所有整数

如何使用netcat为DPDK实例提供输入?

如何拆分和计算 Bash 中单词的出现次数?

仅在 Linux 上出现 AWS RDS `flush tables` 错误的 mysqldump

有必要注意非错误提示吗?好像没有找到包裹‘***’?

在 Linux 中 Select 多个同名的可执行文件

未找到框架.NETFramework,Version=v4.7.1的参考程序集

我需要 -D_REENTRANT 和 -pthreads 吗?

Eclipse 的 C# 插件

bash 中的sed命令

从 Linux shell 将多个文件从一个目录复制到另一个目录

如何在 linux ElementaryOS 中修复 Genymotion,但未找到错误CXXABI_1.3.8

仅在不存在时添加换行符

你如何在 C 中的 Linux 上进行非阻塞控制台 I/O?

为什么导入 SQL 这么慢?

自动化 Amazon EBS 快照 任何人在 linux 上都有一个好的脚本或解决方案

Windows 开发环境值得付出代价吗?

如何安装python开发者包?