Skip to main content
 首页 » 编程设计

c++之使用 std::cout 的多线程控制台文本动画

2024年02月03日17zhenyulu

我正在尝试创建一个可以同时将多个字符串动画发送到控制台的函数。我所说的“动画”是指打印一个字符,等待指定的时间,然后打印下一个字符,依此类推。

这是我迄今为止尝试过的:

/** 
 @param msg        Message to animate 
 @param sleep_time Time to wait between each letter 
 @param wait       Whether or not to wait for the current thread to join before returning 
*/ 
void animate(const std::string& msg, const unsigned long long sleep_time, const bool wait = true) 
{ 
    const std::atomic<std::chrono::milliseconds> t_sleep_time = 
        std::chrono::milliseconds(sleep_time); 
 
    std::stringstream msg_strm; 
    msg_strm << msg; 
 
    std::thread animate_thread([&msg_strm, &t_sleep_time]() -> void 
    { 
        char letter; 
 
        while ((letter = msg_strm.get()) != EOF) 
        { 
            std::cout << letter << std::flush; 
            std::this_thread::sleep_for(t_sleep_time.load()); 
        } 
 
        return; 
    }); 
 
    if (wait) 
    { 
        animate_thread.join(); 
    } 
    else 
    { 
        animate_thread.detach(); 
    } 
} 

这是它的驱动程序代码:

int main() 
{ 
    animate("Hello", 500, false); 
    std::cout << '\n' << std::endl; 
    animate("Welcome", 400, true); 
    std::cout << "\n\nEnd" << std::endl; 
} 

这是输出(“Wecome”动画缓慢):

 
Welcome 
 
End 

“你好”怎么了?我对多线程非常陌生,因此非常感谢详细的解释。理想情况下,我希望发生的是在一行上显示“Hello”动画,在下一行上显示“Welcome”动画。这可能吗?

请您参考如下方法:

首先,msg_strm 存在于堆栈中,因此您无法按值将其传递给线程,因为它超出了范围,这就是为什么,最有可能的 Hello 是没有显示。您遇到的另一个问题是您正在调用detach,因此程序可能会在第一个线程完成之前退出。

为了实现您想要做的事情,我建议使用ANSI转义码。因此,以下内容可能不适用于所有命令提示符。另请注意,如果按步骤打印,std::cout 不是线程安全的。

#include <atomic> 
#include <iostream> 
#include <string> 
#include <thread> 
 
std::atomic<int> g_lines = 1; 
 
std::thread animate(const std::string& msg, const unsigned long long sleep_time) 
{ 
    // NOTE: `[=]` means capture all variables used by value. Note that globals 
    // are not captured.  Also note that capture by value is needed because 
    // `msg` can go out-of-scope. 
    return std::thread([=] { 
        auto line = g_lines++; 
 
        for (size_t column = 1; column <= msg.size(); column++) 
        { 
            // using `ANSI escape codes` move the cursor to the correct 
            // position; \x1B[{line};{column}H 
 
            std::cout << "\x1B[" + std::to_string(line) + ";" 
                             + std::to_string(column) + "H" + msg[column - 1]; 
 
            std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time)); 
        } 
    }); 
} 
 
int main() 
{ 
    auto t1 = animate("Hello", 500); 
    auto t2 = animate("Welcome", 400); 
 
    // you need to join all threads else if you call detach, the program might 
    // exit before all threads finish. 
    t1.join(); 
    t2.join(); 
 
    std::cout << "\n\nEnd" << std::endl; 
}