【问题标题】:Multi-thread console text animations with std::cout带有 std::cout 的多线程控制台文本动画
【发布时间】:2019-12-12 08:25:10
【问题描述】:

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

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

/**
 @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”。这可能吗?

【问题讨论】:

  • 控制台只有一个光标。你打算如何让它在字里行间跳跃?
  • @n.'pronouns'm。这很公平哈哈。有没有办法让两个线程通信来完成这个?另外,有没有办法让光标像我需要的那样来回跳跃?
  • 另一方面,如果你为wait 参数传递false,那么animate 函数将在线程仍在运行时返回,这将结束@ 的生命周期987654327@ 和t_sleep_time 变量,这意味着对它们的引用将失效。
  • @Someprogrammerdude 好点。我将在我自己的代码中更改它以通过非原子值捕获(因为没有写入变量)。
  • 有一些非便携式的方式来移动光标。我不知道任何与 iostreams 无缝协作的方法。研究名为“ncurses”的库。

标签: c++ multithreading c++11


【解决方案1】:

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

为了实现你想要做的事情,我建议使用ANSI escape codes。因此,以下可能不适用于所有命令提示符。另请注意,如果您分步打印,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;
}

【讨论】:

  • 你能在你的代码中添加更多的 cmets 吗?由于这对我来说都是新事物,因此可以帮助我了解您在做什么以及为什么这样做。
  • @DrakeJohnson 希望现在更好。
猜你喜欢
  • 1970-01-01
  • 2020-10-02
  • 2013-02-08
  • 1970-01-01
  • 2012-03-09
  • 1970-01-01
  • 2017-04-30
  • 2013-08-19
  • 1970-01-01
相关资源
最近更新 更多