0

Here is what I am trying to do:

  1. Start 10 posix threads from the main thread.
  2. Each of the 10 threads will do some operation say 100 times.

This is what I need help with.

  1. I want to allow thread_1 to do its thing for say 10 seconds (while threads 2-10 are suspended) and then I want to allow thread 2 to do its thing while thread 1 and threads 3-10 are suspended and so on.

How would I do that? I was looking at some pthread tutorials but things seemed complicated even though what I am trying to do is quite straightforward.

0x90
  • 39,472
  • 36
  • 165
  • 245
Ankur Agarwal
  • 23,692
  • 41
  • 137
  • 208
  • 15
    If you want sequential operations, then why create threads at all? Use functions. – Muhammad Waqar May 03 '13 at 22:53
  • 1
    show us your code/ What have you tried? – pyCthon May 03 '13 at 23:02
  • 3
    @WaqarHameed: Perhaps he wants the threads to be scheduled in a round-robin manner. With functions, it's more difficult to pick up where you left off. – Keith Thompson May 03 '13 at 23:07
  • This defeats the purpose of threads. What is the end-goal? – Kevin May 03 '13 at 23:09
  • This is a silly requirement, as explained by @WaqarHameed, and a dup*10000. – Martin James May 03 '13 at 23:47
  • @KeithThompson My requirement is to do switching between threads. – Ankur Agarwal May 04 '13 at 00:14
  • *Why* do you need to switch between threads? Is this a homework assignment? – Keith Thompson May 04 '13 at 00:45
  • @KeithThompson Hey Keith, this is not a homework assignment. – Ankur Agarwal May 04 '13 at 02:00
  • 1
    The whole idea of threads is that, at least conceptually, they're all running *simultaneously* except when synchronization imposes a requirement that precludes simultaneity. Trying to serialize execution of threads based on arbitrary, irrational timeslice choices is not going to work out well. – R.. GitHub STOP HELPING ICE May 04 '13 at 02:43
  • 1
    @abc: There were two questions in my comment. An answer to the first would probably be more useful to those of us who are trying to help you. You might have an [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – Keith Thompson May 04 '13 at 04:35
  • I submit to you that what you're *trying* to do (manually scheduling thread synchronization between an arbitrary number of disparate threads of execution based on a loosely-at-best defined predicate) is many things, but "quite straightforward" is not among them. – WhozCraig May 04 '13 at 06:18

3 Answers3

3

Besides any discussion on whether the OP's question makes sense or not, a possible solution could be the following:

Have a signal handler installed for each thread to be scheduled. This handler is triggered on say SIGUSR1 and internally does nothing more than to invoked a call to pause().

The thread functions all start with a call to pause(), which suspends all threads immediatly after creation.

Create all threads to be schedules using pthread_create(). Stores the pthreads created into an array pthreads.

Assign the first pthread to be run (from pthread) to pthread_first.

To start scheduling call pthread_kill(pthread_first, SIGUSR2) to resume the thread to be run first (by makeing the pause() its blocking on to return). Make pthread_current become pthread_first.

To actually perform scheduling an additional thread (perhaps the main thread) loops infinitly and calls sleep(SCHEDULING_INTERVALL) and then calls pthread_kill(pthread_current, SIGUSR1) to suspend the current thread (by invoking its signal handler and with this running into pause()). Then call pthread_kill(pthread_next, SIGUSR2) to resume the next thread (by makeing the pause() its blocking on to return). Make pthreat_current become pthread_next, and pthread_next become another entry from the array pthread filled during thread creation.

But be aware:

As the threads interupted by signals and suspended by pause() might have been caught in the middle of doing some work on shared resources and stay their until resumed the chances of stepping on each other toes are quiet high.

To all others: yes, beat me ;-)


Update:

Equivalent example:

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>

#define THREADSMAX (3)
#define SCHEDULING_INTERVALL (5) /* seconds */

void
sigusr_handler(int signo)
{
  if (SIGUSR1 == signo)
    {
      pause();
    }
}

void *
thread_function(void * pv)
{
  intptr_t iThread = (intptr_t) pv;

  pause();

    {
      int i = 0;
      for (;;)
        {
          printf("%d: %d\n", (int) iThread, i++);
          sleep(1);
        }
    }

  pthread_exit(NULL);
}

int
main(int argc, char ** argv)
{
  struct sigaction signal_action;
  memset(&signal_action, 0, sizeof(signal_action));
  signal_action.sa_handler = sigusr_handler;
  sigemptyset(&signal_action.sa_mask);

  sigaction(SIGUSR1, &signal_action, NULL);
  sigaction(SIGUSR2, &signal_action, NULL);

    {
      pthread_t threads[THREADSMAX] =
        { 0 };

      intptr_t iThread = 0;

      /* create threads */
      for (; iThread < THREADSMAX; ++iThread)
        {
          int iResult = pthread_create(&threads[iThread], NULL, thread_function,
              (void *) iThread);
          if (iResult)
            {
              errno = iResult;
              perror("pthread_created()");
              exit(1);
            }
        }

      sleep(1); /* Unreliable workaround: Try to make sure all threads have started and block in "pause()". See comments on how this might be fixed nicely ... */

      /* scheduling loop */
      for (iThread = 0;; ++iThread)
        {
          if (THREADSMAX == iThread)
            {
              iThread = 0;
            }
            /* Resume current thread */
            {
              int iResult = pthread_kill(threads[iThread], SIGUSR2);
              if (iResult)
                {
                  errno = iResult;
                  perror("pthread_kill(..., SIGUSR2)");
                  exit(2);
                }
            }

          sleep(SCHEDULING_INTERVALL);

          /* Suspend current thread */
            {
              int iResult = pthread_kill(threads[iThread], SIGUSR1);
              if (iResult)
                {
                  errno = iResult;
                  perror("pthread_kill(..., SIGUSR1)");
                  exit(3);
                }
            }
        }
    }

  return 0;
}

Expected output:

0: 0
0: 1
0: 2
0: 3
0: 4
1: 0
1: 1
1: 2
1: 3
1: 4
2: 0
2: 1
2: 2
2: 3
2: 4
0: 5
0: 6
0: 7
0: 8
0: 9
1: 5
1: 6
1: 7
1: 8
1: 9
2: 5
2: 6
...
alk
  • 69,737
  • 10
  • 105
  • 255
  • 1
    I lost you at "Have a signal handler installed for each thread to be scheduled." Signal handlers are process-global, not per-thread. Signals are delivered to specific threads, but the same handler is used in each thread. Aside from that misstatement, however, your approach seems to work; you don't seem to rely on having different handlers per-thread. – R.. GitHub STOP HELPING ICE May 04 '13 at 13:37
  • Actually, one detail: `pause` is not what you want; any use of it similar to what you're trying to do has race conditions and false positives. You should instead be using `sigwaitinfo`. – R.. GitHub STOP HELPING ICE May 04 '13 at 13:41
  • @R..: Yes, I am inaccurate in my wording. I wanted to point out that all threads shall have their signal masks set up in to handle `SIGUSR1` the same way, use the same handler. – alk May 04 '13 at 14:32
  • @R..: There are races due to the fact, that it is not clear when a thread actually has started. Those could be handled by a condition/mutex per thread. But those races do not actually refer to the OP's problem. I do not see other races, at least as long I assume no other signals are involved. However, using `sigwaitinfo()` is more flexible indeed. – alk May 04 '13 at 14:41
  • The race is simple: there's no guarantee when you call `pause` that the signal has not *already* arrived, in which case you'll pause forever (or at least until the next signal comes around). In this particular case, the signal arriving before `pause` is called would only happen under extremely high system load (e.g. heavy swapping). In any case, just use `sigwaitinfo` and be safe. – R.. GitHub STOP HELPING ICE May 04 '13 at 17:08
1
  1. POSIX Threads: Condition Variables - what's the point?
  2. Joining and Detaching Threads: enter image description here
  3. Using sleep() and the scheduler will context switch to another process. IMO it isn't so bad solution - it is not generic since in some scenario 100ms is a lot of time and on another it is very short.
  4. Implement by ourself condition variables or some sort of synchronizer- but it is really isn't recommended.
Community
  • 1
  • 1
0x90
  • 39,472
  • 36
  • 165
  • 245
1

You have not stated your requirements well. Do the threads all operate on independent data (with no need for synchronization between them)? If so, the whole idea of trying to do your own course scheduling in 10-second units is nonsensical. Just let them all run and do their thing. Of course it would be fairly easy to achieve what you asked for using timer signals and controlling which threads have the signal blocked, but it would also be utterly useless.

If on the other hand your threads do have data dependencies between one another, then any pattern of "run for 10 seconds then transfer control to another thread" is invalid; it could lead to deadlock if the next thread cannot proceed because of a lock the first thread holds. (I suppose it's not entirely dead-lock since control will eventually return to the first thread, but the delays could grow on a very large order if there are multiple locks involved.) Instead, in this case, you should be aiming to have which thread is running controlled by the flow of data.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711