3

I am looking for a way(preferably with boost threads), to interrupt a thread if it has not joined. I start multiple threads, and would like to end any of them that have not finished by 200 milliseconds. I tried something like this

boost::thread_group tgroup;
tgroup.create_thread(boost::bind(&print_f));
tgroup.create_thread(boost::bind(&print_g));
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
tgroup.interrupt_all();

Now this works, and all threads are ended after 200 milliseconds; however I would like to try and join these threads if they finish before 200 milliseconds, is there a way to join and interrupt if not finished by a certain amount of time?

Edit: reason why I need join to happen before timeout:

I am creating a server where speed is very important. Unfortunately I have to make requests to other servers for some information. So I would like to make these calls in parallel, and finish as soon as possible. If a server is taking too long, I have to just ignore the information coming from that server, and continue on without it. So my timeout time is my maximum amount of time I can wait. It will be extremely beneficial to me to be able to continue on with contemplation when all responses are received, instead of waiting for time timeout timer. So what my program will:

-Get a request from a client.

-Parse information.

Create threads

-Send information to multiple other servers.

-Get information back from servers.

-Put information from servers on a shared queue.

End Threads

-Parse information from shared queue.

-Return information back to client

Eumcoz
  • 2,388
  • 1
  • 21
  • 44
  • how does the missing information impacts the computation of the result? Do you plan to wait the maximum time anyway if some information is still missing? – didierc Apr 10 '13 at 18:04
  • Missing information does not have a great effect. You can think of it is a bid. Each server will reply with a bid given the information I supply them. If they do not return anything in time, they don't get to bid. Ideally I would like all servers to respond since I will have the most amount of bids, and most amount of choices available, but it is no problem if a server does not respond in time. Hence the just cut my open connections and move on strategy. – Eumcoz Apr 10 '13 at 18:48

2 Answers2

3

If you're using a very recent Boost and C++11, use try_join_for() (http://www.boost.org/doc/libs/1_53_0/doc/html/thread/thread_management.html#thread.thread_management.thread.try_join_for). Otherwise, use timed_join() (http://www.boost.org/doc/libs/1_53_0/doc/html/thread/thread_management.html#thread.thread_management.thread.timed_join).

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Can timed_join or try_join_for be used in conjunction with a thread_group. Or is there a good way to setup multiple single threads to end at the same time with these(I believe these block)? – Eumcoz Apr 10 '13 at 15:04
  • @Eumcoz `boost::thread_group` has fewer interfaces than `boost::thread`, and it does not have interfaces named `timed_join` or `try_join_for` or `try_join_until`. – zeekvfu Sep 02 '13 at 10:04
3

What you want to use is probably a set of scoped threads, and call terminate on all the remaining threads after timeout. thread groups and scoped threads are not useable together unfortunately.

The thread group class is actually a very simple container: you cannot remove a thread of it if you don't have a pointer to it already, and you cannot get a pointer to a thread which has been created by the group. The class API doesn't provide much either. This is a bit hindering for management in your situation.

The remaining solutions rely on creating the threads outside the goup, and have each of them do a specific task just before finishing. It could:

  • remove itself from the group,
  • then add itself to another group

The managing thread will have to call join_all on the later group, and act as before with the former.

using namespace boost;
void thread_end(auto &thmap, thread_group& t1, thread_group& t2, auto &task){
    task();
    thread *self = thmap[this_thread::get_id()];
    t1.remove_thread(&self);
    t2.add_thread(&self);
}

 std::map<thread::id, thread *> thmap;
thread_group trunninggroup;
thread_group tfinishedgroup;
thread *th;
th = new thread(
    bind(&thread_end, thmap, trunninggroup, tfinishedgroup, bind(&print_f)));
thmap[th->get_id()] = th;
trunninggroup.add_thread(th);
th = new thread(
    bind(&thread_end, thmap, trunning_group, tfinishedgroup, bind(&print_g)));
thmap[th->get_id()] = th;
trunninggroup.add_thread(th);
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
tfinishedgroup.join_all();
trunninggroup.interrupt_all();

But this is not ideal if you actually want the managing thread to be notified of a thread end when it actually happens (and I'm not really certain it does anything useful anyway). A solution for getting notified is perhaps to:

  • do the group migration as above
  • then trigger a condition variable on which the management thread is doing a timed_wait

but you will have to do some time computation to keep track of the remaining time after being notified, and resume sleep with that time left. That would be entirely dependent on the Duration class used for that task.

Update

Seeing the big picture, I would try a completely different approach: I don't think that terminating a thread which is already finished is a problem, so I would leave them all in the group, and use the group to create them, as your code demonstrate it.

However, I would try to wake up the managing thread as soon as all the threads are done, or after timeout. This is not doable with what the thread_group class offers alone, but it can be done with a custom made semaphore, or a patched version of boost::barrier to allow a timed wait.

Basically, you set a barrier to the number of threads in the group plus one (the main thread), and have the main thread time wait on it. Each worker thread does its work, and when finished, post its result in the queue, and wait on the barrier. If all the worker threads finish their task, everyone will wait and the barrier gets triggered.

Then main thread (as well as all others, but it doesn't matter), wakes up and can proceed by terminating the group and process the result. Otherwise, it will be awaken at timeout and do the same anyway.

The patching of boost::barrier should not be too difficult, you should only need to duplicate the wait method and replace the condition variable wait inside by a timed_wait (I didn't look at the code, this assumption might be totally of the mark though). Otherwise I provided a sample semaphore implementation for this question, which shouldn't be difficult to patch either.

Some last consideration: terminating a thread is usually not the best approach. You should instead try to signal the threads they have to abort, and wait for them, or somehow havecthem pass their unfinished task to an auxiliary thread which should clean things up serially. Then your thread group would be ready to tackle on the next task, and you wouldn't have to destroy and create threads all the time, which is a somewhzt costly operation. It will require to formalize the idea of a task in the context of your application, and make the threads run on a loop for taking new tasks and process them.

Community
  • 1
  • 1
didierc
  • 14,572
  • 3
  • 32
  • 52
  • Thank you. I will look into scoped threads and see if that is feasible option. I do not need to use a thread group, my only requirement is that I can have the threads timeout at nearly the same time, but if all threads complete to end when done. I would rather not do time tracking if possible, but if I have to, I guess I have too. It seems like scoped threads are fairly new too boost, do you know of any good examples I can take a look at while I upgrade my boost version? – Eumcoz Apr 10 '13 at 15:44
  • 1
    corrected my answer to address the issue of accessing the thread instance from the callable it executes. It's a tad more involved. I guess you'd be better of not using the group container, but instead a normal one, and call terminate by hand on each remaining threads at timeout. kind of crude, but at this time thread management in C++ is still difficult. Perhaps you could look into threadpools. – didierc Apr 10 '13 at 16:14
  • I don't think your code will work for me unfortunately. I still has a 200ms sleep in it, unless I am reading it incorrectly. After thinking about it a bit more, I might be better off using a deadline_timer, while trying to join_all. I'll have the deadline_timer interrupt the thread_group if it hits its time. If not, I will cancel the timer. I believe this should work. If it doesn't work, I will manage timer with a timed_join I think, I will just calculate what 200ms is from the current time, then for each join after that I will do a timed join from the new current time to the end time. – Eumcoz Apr 10 '13 at 16:39
  • 1
    I address that issue at the end of my answer. As I said, as it is the code only migrate threads and join them at the end, you need some kind of trigger to wake the main thread and do the `join`. May I ask why you want to do the joins before the timeout? It's not like the `thread` instances will vanish anyway. Perhaps there's some computation result to be retrieved? – didierc Apr 10 '13 at 16:47
  • I see, sorry I misread the end of your answer. I updated my original post with what I am doing. Let me know if you need more information about it. – Eumcoz Apr 10 '13 at 17:12
  • That looks like it is exactly what I am after, thank you very much. Going to mark this as correct, and will update after I have been able to test it. – Eumcoz Apr 10 '13 at 19:04