1

I would like to be able to output information while inputting. For example: Printing a line every second, but also taking user input...

I tried:

#include <iostream>
#include <thread>
#include <string>
#include <unistd.h>
using namespace std;

void foo()
{
    while(1)
    {
        usleep(1000000);
        cout << "Cake\n";
    }
}

int main()
{
    thread t1(foo);
    t1.join();
    string x;
    while(1)
    {
        cin >> x;
        cout << x << "\n";
    }
    return 0;
}

Which outputs:

Cake
Cake
Cake

And then I start typing:

hiCake

When I would like it to be:

Cake
hi

Where the 'hi' is still in the input

If this isn't possible, is there at least a way for me to pause the outputting while there is text being inputted?

I'm using C++11 on Windows 7 using CygWin for the Unix libraries

MUTEX TESTS

Output loop locked:

#include <iostream>
#include <thread>
#include <string>
#include <unistd.h>
#include <mutex>
using namespace std;

mutex mtx;

void foo()
{
    mtx.lock();
    while(1)
    {
        usleep(1000000);
        cout << "Cake\n";
    }
    mtx.unlock();
}

int main()
{
    thread t1(foo);
    t1.join();
    string x;
    while(1)
    {
        cin >> x;
        cout << x << "\n";
    }
    return 0;
}

Input loop locked:

#include <iostream>
#include <thread>
#include <string>
#include <unistd.h>
#include <mutex>
using namespace std;

mutex mtx;

void foo()
{
    while(1)
    {
        usleep(1000000);
        cout << "Cake\n";
    }
}

int main()
{
    thread t1(foo);
    t1.join();
    string x;
    mtx.lock();
    while(1)
    {
        cin >> x;
        cout << x << "\n";
    }
    mtx.unlock();
    return 0;
}

Pure output locked:

#include <iostream>
#include <thread>
#include <string>
#include <unistd.h>
#include <mutex>
using namespace std;

mutex mtx;

void foo()
{
    while(1)
    {
        mtx.lock();
        usleep(1000000);
        cout << "Cake\n";
        mtx.unlock();
    }
}

int main()
{
    thread t1(foo);
    t1.join();
    string x;
    while(1)
    {
        cin >> x;
        cout << x << "\n";
    }
    return 0;
}

Pure input locked:

#include <iostream>
#include <thread>
#include <string>
#include <unistd.h>
#include <mutex>
using namespace std;

mutex mtx;

void foo()
{
    while(1)
    {
        usleep(1000000);
        cout << "Cake\n";
    }
}

int main()
{
    thread t1(foo);
    t1.join();
    string x;
    while(1)
    {
        mtx.lock();
        cin >> x;
        mtx.unlock();
        cout << x << "\n";
    }
    return 0;
}

To save space, I won't paste more code; I did other tests where I made a mutex mtx2 and tried to lock both input (mtx) and output(mtx2) (both pure and the loops). I did also try locking both with mtx (again, both pure and loops)

lewisjb
  • 678
  • 10
  • 26
  • One way to solve it is that you take over the terminal using functions in `#include `, and you print all keypresses manually. So when the user presses a key, the system won't print the corresponding character, but your program will receive the key, and print the character it if and when it decides to do so. – pts Jun 28 '14 at 08:04
  • @pts, I considered that, thanks for the suggestion! I was just hoping there would be a simpler way that I had overlooked – lewisjb Jun 28 '14 at 08:12
  • 1
    the thing that what is dragging you down is the terminal (not the synchronization). The problem is that you use one terminal for two separate asynchronous streams. For instance, what should happen on the terminal if the user is in the middle of the input and right then you need to output something? It’s a problem. The best solution would be to use GUI with an input text box and a **separate** output text box. – bolov Jun 28 '14 at 08:25
  • @bolov, I know that, but I don't want a GUI, nor two terminals. And the 'if the user is in the middle of the input' thing, that's what I'm asking how to fix – lewisjb Jun 28 '14 at 08:29
  • 2
    I'm not writing an answer, but I think, you should consider writing leaner code without synchronization primitives, such as using [Rc.cpp](http://ledentsov.de/2014/06/22/rxcpp-cplusplus-background-ticker-easy/). And anyway, did you think about usability? And in c++ instead of usleep you can use [std::this_thread::sleep_for](http://en.cppreference.com/w/cpp/thread/sleep_for) – Dmitry Ledentsov Jun 28 '14 at 08:37
  • @DmitryLedentsov Thanks for the link! And as for the sleeping, this program was a test, I just wanted functionality, but thanks for the suggestion! – lewisjb Jun 28 '14 at 08:40
  • @Pyro did you solve your problem? If yes, is one of the answers below a correct solution to the problem? If yes please mark it as correct answer. If not please provide an answer of your own. – Dialecticus Jun 30 '14 at 13:53
  • @Dialecticus, no I haven't. – lewisjb Jul 09 '14 at 05:12
  • I don't know if you fixed it already, but `t1.join()` should stand after the loop, and right before `return 0`. It makes very little sense to run a thread, and then immediately wait until it finishes. – Dialecticus Jul 09 '14 at 06:48

5 Answers5

2

Use a mutex to protect your critical sections of code - see http://www.cplusplus.com/reference/mutex/mutex/

Ed Heal
  • 59,252
  • 17
  • 87
  • 127
  • Correct answer, unless OP wants process able to print while the user is in input. For instance, 1) user presses `h`, output is `h`, 2) process wants to write `Cake\n`, output is `Cake\nh`, 3) user presses `i`, output is `Cake\nhi`. – Dialecticus Jun 28 '14 at 08:08
  • Thanks for the suggestion, however I just tried it (locking output, locking input, both) but none worked sadly, it gave the same results. – lewisjb Jun 28 '14 at 08:09
  • 1
    @Pyro edit and expand your question so that we can see what you tried. – Dialecticus Jun 28 '14 at 08:11
  • @Pyro there has to be only one mutex, and this one mutex must guard all calls involving `cin` and `cout`. All of them, no exceptions. Try that :) – Dialecticus Jun 28 '14 at 08:22
  • @Dialecticus, I thought I had, but I did it again just to check (every `cin` and `cout` call was preceded with `mtx.lock()` and had `mtx.unlock()` after, but still same results. – lewisjb Jun 28 '14 at 08:24
1

Your code with pure output locked is almost right solution. What needs to be done is to guard both "cout" statements with mutexes and move std::thread::join behind while loop in main or it will never get executed:

using namespace std;

mutex mtx;

void foo()
{
    while(1)
    {
        usleep(1000000);
        mtx.lock(); // we want mutex to be held only for time of cout, not time of wait
        cout << "Cake\n";
        mtx.unlock();
    }
}

int main()
{
    thread t1(foo);
    string x;
    while(1)
    {
        mtx.lock(); // we lock second occurence of cout and cin not to get interrupted by foo
        cin >> x;
        cout << x << "\n";
        mtx.unlock(); 
    }
    t1.join(); // thread join stops the calling thread until called thread is finished
    // so we join the thread just before our program finishes
    return 0;
}

This way you will get output like:

Cake
Hi
Hi

Why we don't lock the foo() for the time of usleep? Because it would lock mutex for longer than necessary.

Why I opt to not locking mtx before cin? Because time of cin is highly undefined. And if foo() was performing any important action it could be paused for pretty long time.

Personally I recommend you reading on the book C++ Concurrency in Action by Anthony Williams. Great reading in my opinion.

Łukasz Daniluk
  • 460
  • 3
  • 9
  • Thanks for the suggestion! But when I tested your second solution (with your code) I got the outputs you said. And I would prefer to make the input written on screen. – lewisjb Jun 28 '14 at 09:02
  • So you want to get `Hi \n Cake` or `Hi \n Hi \n Cake` ? – Łukasz Daniluk Jun 28 '14 at 09:03
  • To get `Hi \n Cake` you should use first solution mentioned in my answer *or* remove `cout` line from `main`. For `Hi \n Hi \n Cake` you should *move* `mtx.lock()` in `main` to `optional place to lock` (just before `cin`). – Łukasz Daniluk Jun 28 '14 at 09:08
  • I want to get `Cake \n Hi` but where `Hi` is still in the input (so I can keep typing), because I haven't pressed enter yet – lewisjb Jun 28 '14 at 09:09
  • Did you *move* `mtx.lock()` call before `cin`? – Łukasz Daniluk Jun 28 '14 at 09:10
  • Yes, and I tried locking both the `cin` and the `cout`, both give similar outputs (and both wait for me to input something before outputting) – lewisjb Jun 28 '14 at 09:13
  • See my updated answer - you will have uninterrupted input and output will wait until you input something. If it is not what you want, then what is? – Łukasz Daniluk Jun 28 '14 at 09:14
  • Sorry if I've caused you confusion; I want it so that it outputs, without interrupting the input (so it can output while I'm still typing). If that isn't possible then I'd want it to not output if there is something in the input, e.g. If I haven't typed anything it outputs, if I have started typing it waits until I've pressed enter – lewisjb Jun 28 '14 at 09:17
  • It will not output when there's something in the input already. MAYBE YOU INPUT WITH SPACES! Then you should use `std::getline( cin, x )` instead of `cin >> x` :) But `Cake` still won't be printed even if there is no input on the screen. As far as I am concerned there is no portable way to check if `cin` has any contents. `Cake` will be printed only between iterations of `while` loop in `main`. – Łukasz Daniluk Jun 28 '14 at 09:24
  • Thanks, I was hoping that wasn't the case – lewisjb Jun 28 '14 at 09:25
0

Use a "printing" object that synchronizes access to cout:

struct SyncCOUT{
  SyncCOUT(): m_mutex() {}
  std::mutex m_mutex;
  std::ostream & operator<<(const char * _str)
  {
    m_mutex.lock();
    cout << _str;
    m_mutex.unlock();
    return cout;
  }
};

SyncCOUT g_scout;

// and then in both threads use this global synchronized "cout" wrapper like this:
// g_scout << "Hello, world!\n";
YePhIcK
  • 5,816
  • 2
  • 27
  • 52
  • Thanks for the suggestion! But I tried it and it gave errors, which I believe are related to the operator overloading – lewisjb Jun 28 '14 at 08:35
  • I did not compile the code - it was just to give you an idea of how to go about this problem in general – YePhIcK Jun 28 '14 at 08:38
  • Oh, I see it - I forgot to return `cout` from my `operator<<`, duh! :) – YePhIcK Jun 28 '14 at 08:40
  • The signature of `operator<<` was indeed wrong. Sadly can't help you beyond this as my environment (MSVC 2010) doesn't have C++11 support for :( – YePhIcK Jun 28 '14 at 09:13
0

For this to work, you will need to do few things:

  1. Create two threads, one will write text on standard output, another will read the text from the standard input.

  2. Make the output thread lock a mutex before writing, and unlock it after writing. This writing operation will be your "critical section".

  3. Inside the read thread, read the data character by character directly from the terminal. On Unix you would use the curses library for this task (probably the getch() function), on Windows you would probably use ReadFile on the handle acquired from GetStdHandle(STD_INPUT_HANDLE).

  4. Then you could create a simple state machine: if the user presses the key that is not enter, try to lock the mutex. If the user presses the enter key, unlock the mutex.

This way it will be possible for the write thread to output text only after the user will press the enter key. When the user will be in the middle of inputting text (that is, before pressing enter key), the mutex will block writing operation until the user presses the enter key.

This approach would probably need to be expanded to also handle pressing the backspace key.

antonone
  • 2,045
  • 1
  • 25
  • 35
0

You likely need to explicitly synchronize std::cout by calling std::cout.flush().

This appears to be an issue with the implementation of the console you are running under. Some consoles, such as cmd.exe and powershell have an implicit flush() synchronization performed after each insertion (<<) operation on std::cout. I was concerned about the thread code I was testing having a concurrency problem until I discovered it worked as expected under cmd.exe and powershell, but not under MINGW64/MSYS2 (Git for Windows deployment).

without the flush call my two threads were not interleaving characters as expected, and in fact were not outputting characters at all until the worker thread was .join()ed until I added the explicit std::cout.flush(). After that was added all three consoles had the same behavior.

Tzalumen
  • 652
  • 3
  • 16