7

I am learning the new multi-threading techniques in C++11. Almost all the tutorials I read on the web is teaching how to launch a new thread(or several threads) executing a function, how to join (or detach) the thread(or threads) later and how to avoid racing conditions using mutex, etc.

But I don't see any of them showing how to make a thread execute several functions at different parts of the program. The question is, with C++11 threads, is it possible to achieve the following? If so, how? (Giving an example will be great).

void func1(std::vector<int> & data1){ ... }

void func2(std::vector<int> & data2){ ... }

//  main function version I
int main(){
   std::vector<int> data1; 
   // prepare data1 for func1;
   std::thread t1(func1, std::ref(data1));

   std::vector<int> data2; 
   // prepare data2 for func2;
   if (func1 in t1 is done){ 
         t1(func2, std::ref(data2)); 
   }

   t1.join();
   return 0;      
}

And further, what if I want to put the the above main function body into a loop, as following. Is it possible? If so, how?

//main function version II
int main(){
   std::vector<int> bigdata1;
   std::vector<int> bigdata2;

   std::thread t1; // Can I do this without telling t1 the function 
                   // to be executed?

   for(int i=0; i<10; ++i){
       // main thread prepare small chunk smalldata1 from bigdata1 for func1;

       if(t1 is ready to execute a function){t1(func1, std::ref(smalldata1));}

       // main thread do other stuff, and prepare small chunk smalldata2 from bigdata2 for func2;

       if (func1 in t1 is done){ 
            t1(func2, std::ref(smalldata2)); 
       }
   }

   t1.join();
   return 0;      
}
Yuchen
  • 30,852
  • 26
  • 164
  • 234
Allanqunzi
  • 3,230
  • 1
  • 26
  • 58
  • 4
    Why this question gets downvoted? Is this not a legitimate question to ask as a beginner for learning C++11 multithreading? – Allanqunzi May 12 '15 at 01:13
  • I'm not an expert at all in threading, but I think you may use a [`std::condition_variable`](http://en.cppreference.com/w/cpp/thread/condition_variable) to make the second thread wait for the first one. Also, if you can get your hands on [this book](http://www.amazon.com/C-Concurrency-Action-Practical-Multithreading/dp/1933988770) you'll probably learn a lot from the person who implemented most of the `std::thread`. – vsoftco May 12 '15 at 01:38
  • 1
    What you usually want for a situation like this is a thread-safe queue of tasks, and a thread pool executing tasks from the queue. The 'main" thread packages up a task and pushes it into the queue. The threads in the pool wait for something to appear in the queue, and when it does they get a task, execute it, then wait for the queue again. – Jerry Coffin May 12 '15 at 01:53
  • @vsoftco, thanks for letting me know about the book, I will put in my list as I don't have time to read for now. Jerry Coffin, I think you are right, I will look it up if there are implementation examples in C++11 or you can suggest any if you know. – Allanqunzi May 12 '15 at 02:03
  • Not a valid answer to the question, but what if the function contains a state machine and doesn't end until the state machine lets it? Eliminates the need to have synchronization across multiple threads in most cases. If you absolutely have to, then one `std::atomic` variable maintains state that main can mess with. Check state, do task, repeat... If the task's not done yet, no state check and no progressing into the next task. If the next task's not ready, park in an idle state that's basically a short sleep. – user4581301 May 12 '15 at 02:36

2 Answers2

2

Reference from cplusplus.com:

default constructor constructs a thread object that does not represent any thread of execution.

Therefore std::thread t simply doesn't define an executable thread. A thread function has to be provided when creating the thread and it cannot be set afterwards.

For your main function version I, you will have to create two threads. Something as the following:

int main(){
    std::vector<int> data1; 
    // prepare data1 for func1;
    std::thread t1(func1, std::ref(data1));

    std::vector<int> data2; 
    // prepare data2 for func2;
    t1.join(); // this is how you wait till func1 is done

    // you will have to create a new thread here for func2
    std::thread t2(func2, std::ref(data2)); 
    t2.join(); // wait for thread2 (func2) to end

    return 0;      
}

Similarly, you can put them in a loop and it is alright which will give you main function version II.

Yuchen
  • 30,852
  • 26
  • 164
  • 234
  • So, there is no way to achieve this with one thread. For the main function version II, at the end of the loop, after the `t1.join()` and `t2.join()`, do you think I can still use `t1` and `t2` in the next iteration? Or do I need to kill `t1` and `t2` and redefine for the next iteration? What happens after you call `join`? And how do you a kill a thread ? – Allanqunzi May 12 '15 at 02:35
  • @Allanqunzi Nope, no way to do it in one thread. Nope, you cannot reuse `t1` and `t2` anymore after `join`. After a call to `joint`, the thread object becomes non-joinable and is literally dead already. For you last question: you cannot kill the thread in C++ ([reference](http://stackoverflow.com/questions/13893060/i-want-to-kill-a-stdthread-using-its-thread-object)). The given reference explains why. – Yuchen May 12 '15 at 02:42
  • Thanks! I think you have a typo `joint` should be `join`. – Allanqunzi May 12 '15 at 03:28
  • @Allanqunzi, yay, you are right. Unfortunately, cannot edit the comment anymore. Time out. – Yuchen May 12 '15 at 03:46
1

C++11 threading are primitives intended to allow you to write real libraries. They are not easy to use. You should wrap them.

Something like:

struct tasks {
  std::mutex m;
  std::condition_variable v;
  std::vector<std::packaged_task<void>> work;
  std::vector<std::future<void>> finished;
  void operator()(){
    while(true){
      std::packaged_task<void> f;
      {
        std::unique_lock<std::mutex> l(m);
        if (work.empty()){
          v.wait(l,[&]{return !work.empty();});
        }
        f = std::move(work.front());
        work.pop_front();
      }
      if (!f.valid()) return;
      f();
    }
  }
  std::future<void> do(std::function<void()> f){
    std::packaged_task<void> p(f);
    auto r=p.get_future();
    {
      std::unique_lock<std::mutex> l(m);
      work.push_back(std::move(p));
      v.notify_one();
    }
    return r;
  }
  void start(){
    finished.push_back(std::async(std::launch_policy::async,
      std::ref(*this)));
  }
  ~tasks(){
    std::unique_lock<std::mutex> l(m);
    for(auto&&unused:finished){
      work.push_back({});
    }
    v.notify_all();
  }
};

use looks like:

int main(){
  tasks t;
  t.start();
  t.do([]({std::cout<<"hello ";});
  t.do([]({std::cout<<"world\n";});
}

if you want to know when a task is done, check the future do returns.

Written on phone, not compiled, probably full of typos and errors, but a place to start.

Does not support abort early. Easy to write abaondon which empties work.

Supports multiple consumers (workwe threads) I suspect. On compliant system, at dtor will wait for all threads to finish the queued jobs. Not on MSVC2013 tho.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • thanks for replying. Is this a thread pool implementation? As a beginner, I think I need to read more to understand your code. – Allanqunzi May 12 '15 at 03:27
  • @allan was originally a worker task thing, with one thread eating tasks, but the pool/ability to handle multiple threads sort of fell out. It isn't well written (the `work` vector queue should be something else, maybe a deque, maybe something else), but the basic produce-consume-notify structure is there. – Yakk - Adam Nevraumont May 12 '15 at 10:45