// C program to demonstrate working of Semaphores
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
sem_t mutex;
void* thread(void* arg)
{
//wait
sem_wait(&mutex);
printf("\nEntered..\n");
//critical section
sleep(4);
//signal
printf("\nJust Exiting...\n");
sem_post(&mutex);
}
int main()
{
sem_init(&mutex, 0, 1);
pthread_t t1,t2;
pthread_create(&t1,NULL,thread,NULL);
sleep(2);
pthread_create(&t2,NULL,thread,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
sem_destroy(&mutex);
return 0;
}
basically, I have 16 threads, and their synchronization looks like the above code. However, in main() {} I have this line
int main()
{...
/*
* wait untill tip has completed its process
*/
wait_for_tip_completed(tip);
wait_for_tip_completed is an active waiting loop
void wait_for_tip_completed( ....
while (1)
{
if (tip.completed) return;
}
This somehow works, but ... it doesn't look the right way.
POSIX threads, my favourite!
Let's say you want the created threads to block at a specific point, until main thread has reached that point. The simplest way to achieve this is to use a shared global mutex, that the main thread locks before creating the threads, and unlocks when it reaches the specified point. The created threads simply grab that mutex and release it immediately. If the main thread has not reached that point yet, then the threads will block (wait at that point, without consuming CPU time) until the main thread unlocks the thread.
Example:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <inttypes.h>
#include <stdio.h>
#include <pthread.h>
#include <limits.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#ifndef NTHREADS
#define NTHREADS 5
#endif
#ifndef MSDELAY
#define MSDELAY 500
#endif
static pthread_mutex_t main_point = PTHREAD_MUTEX_INITIALIZER;
static void *worker(void *payload)
{
const int id = (intptr_t)payload;
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
printf("%ld.%09ld: Thread %d grabbing mutex.\n", (long)(now.tv_sec), now.tv_nsec, id);
fflush(stdout);
pthread_mutex_lock(&main_point);
pthread_mutex_unlock(&main_point);
clock_gettime(CLOCK_REALTIME, &now);
printf("%ld.%09ld: Thread %d continuing.\n", (long)(now.tv_sec), now.tv_nsec, id);
fflush(stdout);
return NULL;
}
static unsigned long millisleep(unsigned long msecs)
{
struct timespec req, rem;
req.tv_sec = msecs / 1000;
req.tv_nsec = (msecs % 1000) * 1000000;
if (nanosleep(&req, &rem) == -1 && errno == EINTR)
return 1000 * (unsigned long)rem.tv_sec + rem.tv_nsec / 1000000;
return 0;
}
int main(void)
{
pthread_t ptid[NTHREADS];
pthread_attr_t attrs;
struct timespec now;
int i, result;
if (pthread_attr_init(&attrs)) {
fprintf(stderr, "Cannot initialize pthread attributes: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* These worker threads do not need more than the minimum stack.
Create an attribute to tell the C library about this setting. */
pthread_attr_setstacksize(&attrs, PTHREAD_STACK_MIN);
/* Lock the main_point mutex. */
pthread_mutex_lock(&main_point);
clock_gettime(CLOCK_REALTIME, &now);
printf("%ld.%09ld: Main thread started.\n", (long)(now.tv_sec), now.tv_nsec);
fflush(stdout);
for (i = 0; i < NTHREADS; i++) {
result = pthread_create(&(ptid[i]), &attrs, worker, (void *)(intptr_t)(i + 1));
if (result) {
fprintf(stderr, "Failed to create thread %d of %d: %s.\n", i + 1, NTHREADS, strerror(errno));
return EXIT_FAILURE;
}
}
clock_gettime(CLOCK_REALTIME, &now);
printf("%ld.%09ld: Main thread sleeping for %d milliseconds.\n",
(long)(now.tv_sec), now.tv_nsec, MSDELAY);
fflush(stdout);
millisleep(MSDELAY);
clock_gettime(CLOCK_REALTIME, &now);
printf("%ld.%09ld: Main thread woken up; releasing mutex...\n", (long)(now.tv_sec), now.tv_nsec);
fflush(stdout);
pthread_mutex_unlock(&main_point);
clock_gettime(CLOCK_REALTIME, &now);
printf("%ld.%09ld: Main thread mutex released. Reaping worker threads...\n", (long)(now.tv_sec), now.tv_nsec);
fflush(stdout);
for (i = 0; i < NTHREADS; i++) {
result = pthread_join(ptid[i], NULL);
if (result) {
fprintf(stderr, "Warning: Could not reap thread %d of %d: %s.\n", i + 1, NTHREADS, strerror(result));
}
}
clock_gettime(CLOCK_REALTIME, &now);
printf("%ld.%09ld: All done.\n", (long)(now.tv_sec), now.tv_nsec);
fflush(stdout);
return EXIT_SUCCESS;
}
Compile this using e.g.
gcc -Wall -O2 -DNTHREADS=8 -DMSDELAY=250 example.c -lpthread -o example
and run using e.g.
./example | sort -n
The NTHREADS defines the number of threads, and MSDELAY how long the main thread sleeps after creating the threads. The output timestamps are in seconds since Unix Epoch, real time. The sort -n command sorts the output according to the timestamps.
On my system, this outputs
1585648512.613949989: Main thread started.
1585648512.614027646: Thread 1 grabbing mutex.
1585648512.614046279: Thread 2 grabbing mutex.
1585648512.614060944: Thread 3 grabbing mutex.
1585648512.614078104: Thread 4 grabbing mutex.
1585648512.614093435: Thread 5 grabbing mutex.
1585648512.614136953: Main thread sleeping for 250 milliseconds.
1585648512.614142279: Thread 6 grabbing mutex.
1585648512.614146957: Thread 7 grabbing mutex.
1585648512.614157421: Thread 8 grabbing mutex.
1585648512.864261296: Main thread woken up; releasing mutex...
1585648512.864304643: Main thread mutex released. Reaping worker threads...
1585648512.864377347: Thread 1 continuing.
1585648512.864460867: Thread 2 continuing.
1585648512.864515911: Thread 3 continuing.
1585648512.864538436: Thread 4 continuing.
1585648512.864579944: Thread 5 continuing.
1585648512.864601765: Thread 6 continuing.
1585648512.864620224: Thread 8 continuing.
1585648512.864649748: Thread 7 continuing.
1585648512.864709077: All done.
which shows that all eight threads continued within 388 microseconds (0.000388 seconds) of the main thread releasing the mutex.
This works for situations where you have a "checkpoint" where you want threads to wait, until a specific thread lifts that "checkpoint".