6
int main() {
thread t1([] {printer("*", 100); });
thread t2([] {printer("+", 100); });
t1.join();
t2.join();
}

void printer(string c, int num)
{
  for (int i = 1; i <= num; i++)
  {
    cout << c;
  }
cout << endl;
}

Right now this prints something like ****+++** I want it to print *** all in one line then +++ all in one line. We are not allowed to use mutex locks or block thread access to the printer function. The code must still be multithreaded.

Any ideas on how to do this?

Charlie Brumbaugh
  • 219
  • 1
  • 5
  • 15
bananas
  • 83
  • 3
  • 3
    Simple - do not start second thread until first is finished. – Slava Oct 18 '17 at 20:09
  • I did that by putting t1.join() after creating thread t1, if that is what you mean. I was told this essentially violates multithreading since you're creating a thread and then ending it. – bananas Oct 18 '17 at 20:11
  • 1
    From the phrasing, it sounds like this is a homework assignment, so the constraints here have to be met. But in general, if you have requirements to do one task to completion then do another task, that's not a good fit for multiple threads which, after all, are about simultaneous execution. – Pete Becker Oct 18 '17 at 20:12
  • 3
    @bananas -- in response to your comment, yes, that's inconsistent with multi-threading, and that's because the **requirements** are inconsistent with multi-threading. (My previous comment was written before your latest comment) – Pete Becker Oct 18 '17 at 20:13
  • 2
    sounds like a class assignment where the teacher has got some special trick in mind. Its like the usual junk 'do x without using a,b, c' where a,b,c are the correct , normal ways to do things – pm100 Oct 18 '17 at 20:25
  • if the teacher tells you the answer you should post it here so we can all see what the trick is – pm100 Oct 18 '17 at 20:26
  • @pm100 -- the answer by Slava is the "trick". Despite my comments, this is a legitimate solution, and not an absurd requirement. – Pete Becker Oct 18 '17 at 20:28
  • 1
    "*not allowed to use mutex locks or block threads*" - you know cout writes to stdout, which has a mutex inside right? – rustyx Oct 18 '17 at 20:56
  • Possible duplicate of [Is cout synchronized/thread-safe?](https://stackoverflow.com/questions/6374264/is-cout-synchronized-thread-safe) – jww May 27 '18 at 11:43

4 Answers4

11

Give each printer its own buffer, and print the results from the main:

void printer(ostream& oss, string c, int num) {
    for (int i = 1; i <= num; i++) {
        oss << c;
    }
}

int main() {
    stringstream s1, s2;
    thread t1([&] {printer(s1, "*", 10); });
    thread t2([&] {printer(s2, "+", 10); });
    t1.join();
    t2.join();
    cout << s1.str() << s2.str() << endl;
    return 0;
}

main prepares separate output buffers for each thread, lets each thread fill its buffer concurrently, and waits for the threads to complete. Once both threads return, main prints the results to cout.

Gilles Gouaillardet
  • 8,193
  • 11
  • 24
  • 30
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
9

Accumulate data and then output as one shot:

void printer(string c, int num)
{
     std::string buff;
     for (int i = 1; i <= num; i++)
         buff += c;
     cout << buff << endl;
}
Slava
  • 43,454
  • 1
  • 47
  • 90
  • 1
    Right, I dislike them too. I don't think there's anything wrong with your answer, and I think anyone with cute Лосяш on its user pic deserves an upvote :-) – Sergey Kalinichenko Oct 18 '17 at 20:28
3

Writing to a stringstream first instead of direct output would solve the problem of synchronization:

#include <iostream>
#include <string>
#include <sstream>
#include <thread>

void printer(std::string c, int num) {
  std::stringstream strm;    
  for (int i = 1; i <= num; i++) {
    strm << c;
  }
  std::cout << strm.str() << std::endl;
}

int main() {
  std::thread t1([] {printer("*", 100); });
  std::thread t2([] {printer("+", 100); });
  t1.join();
  t2.join();
}
cwschmidt
  • 1,194
  • 11
  • 17
  • If you would have multiple "<<" operator calls, yes ... but if you just have a single one, it does not interleave, otherwise it would be even impossible without locking and slava's solution will have the very same problem – cwschmidt Oct 18 '17 at 20:27
  • hum.. you maybe right, though i am not convinced ;). have to do some research – 463035818_is_not_an_ai Oct 18 '17 at 20:28
  • Deleting your comment, makes my response meaningless, please research next time, before downvoting ... – cwschmidt Oct 18 '17 at 20:29
  • I retracted my downvote, I dont see a problem with deleting my comment and your response isnt meaningless either. It made me reconsider my statement, so it fullfilled its purpose... – 463035818_is_not_an_ai Oct 18 '17 at 20:34
  • Yes, sure. But new readers don't know, why my response is there, without your initial comment ;-) – cwschmidt Oct 18 '17 at 20:35
  • comments can come and go at any time, if you think the comment is essential for other readers you should add it to the answer (imho it would add quite some clarification) – 463035818_is_not_an_ai Oct 18 '17 at 20:36
2

Have the main thread wait on t1 before starting t2:

thread t1([] {printer("*", 100); });
t1.join();
thread t2([] {printer("+", 100); });
t2.join();
Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • Yes I've already tried this, but was told that this is not a valid solution as you essentially only have one thread running at a time now instead of multiple threads at the same time. – bananas Oct 18 '17 at 20:13
  • Not using any additional threads would be a more efficient and elegant solution. – juanchopanza Oct 18 '17 at 20:15
  • @bananas in that case your task cannot be solved, you cannot have two things concurrently and sequential at the same time – 463035818_is_not_an_ai Oct 18 '17 at 20:15