I am working on a simulator for an embedded operating system that uses 5 threads. One acts as the scheduler thread and the other 4 are worker threads. The order of operation is always the same: the scheduler executes between every other thread and goes on activating the one that should go next, like this:
SchThread Thread1 SchThread Thread2 SchThread Thread3 SchThread Thread4 SchThread Thread1 ... and so on.
This is currently done in Windows with some bad practices and I'm tasked with switching it to a pthreads implementation. I know this is not how threads are supposed to work because the workload is fully sequential, but the simulator relies on having the 5 threads mentioned.
My question is: how am I supposed to implement this behaviour? Are mutexes or conditions enough? Should I use barriers? Maybe signals from the scheduler to wake up worker threads?
I've seen posts like this one (execution of pthreads in a particular order) that works with 2 threads, but I've failed in my attempts to increase the number to 5. Is using pthread_cond_ a good solution to this problem or am I focusing it wrong?
I've also tried doing the synchronization using mutexes or semaphores unsuccessfully. I've thought of a shared variable that manages the turn but I've been unable to make it work properly.
This is an opinion, because there's more than one way to solve your problem.
I would use a shared global variable, currentThread, whose value identifies which thread should run, and I would use a condition variable to let threads notify each other when currentThread changes.
Sorry I don't have time to write an example at this moment, but you can find examples in the man pages for pthread_cond_t, pthread_cond_wait(...), and pthread_cond_broadcast(...)
I've been thinking on asking this for a few days and the day I post the question I find a possible solution to this problem.
Continuing the explanation of the answer linked in the question, the solution looks like this:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> /* sleep */
#define MS100 100000
#define HALFS 500000
pthread_mutex_t m_sch;
pthread_cond_t SCH_GO;
pthread_cond_t THREAD2_GO;
pthread_cond_t THREAD3_GO;
unsigned turno = 1;
/* get next turn */
int sig_turno() {
static int anterior = 0;
int ret;
switch (anterior) {
case 2:
ret = 3;
break;
case 3:
ret = 2;
break;
default: /* first time */
ret = 2;
break;
}
anterior = ret;
return ret;
}
int sch_ret = 0;
void *sch(void *arg) {
int turno_local;
while (1) {
pthread_mutex_lock(&m_sch);
while (turno != 1) {
pthread_cond_wait(&SCH_GO, &m_sch);
}
printf("[THREAD SCH]\n");
usleep(MS100);
turno_local = sig_turno();
turno = turno_local;
switch (turno_local) {
case 2:
pthread_cond_signal(&THREAD2_GO);
break;
case 3:
pthread_cond_signal(&THREAD3_GO);
break;
default:
printf("error.\n");
break;
}
pthread_mutex_unlock(&m_sch);
}
sch_ret = 1;
return &sch_ret;
}
int thread2_ret = 0;
void *thread2(void *arg) {
while (1) {
pthread_mutex_lock(&m_sch);
while (turno != 2) {
pthread_cond_wait(&THREAD2_GO, &m_sch);
}
printf("[THREAD 2]\n");
usleep(HALFS);
turno = 1;
pthread_cond_signal(&SCH_GO);
pthread_mutex_unlock(&m_sch);
}
thread2_ret = 2;
return &thread2_ret;
}
int thread3_ret = 0;
void *thread3(void *arg) {
while (1) {
pthread_mutex_lock(&m_sch);
while (turno != 3) {
pthread_cond_wait(&THREAD3_GO, &m_sch);
}
printf("[THREAD 3]\n");
usleep(HALFS);
turno = 1;
pthread_cond_signal(&SCH_GO);
pthread_mutex_unlock(&m_sch);
}
thread3_ret = 3;
return &thread3_ret;
}
int main() {
void *ret;
pthread_t thread_sch, thread_2, thread_3;
pthread_cond_init(&SCH_GO, NULL);
pthread_cond_init(&THREAD2_GO, NULL);
pthread_cond_init(&THREAD3_GO, NULL);
pthread_mutex_init(&m_sch, NULL);
pthread_create(&thread_sch, NULL, sch, NULL);
usleep(MS100);
pthread_create(&thread_2, NULL, thread2, NULL);
pthread_create(&thread_3, NULL, thread3, NULL);
pthread_join(thread_sch, &ret);
pthread_join(thread_2, &ret);
pthread_join(thread_3, &ret);
printf("main() ending\n");
return 0;
}
Where sch is the scheduler's thread.
This can be extended to more threads and works as expected, even though it's just a draft. I've been unable to obtain similar behaviour using only mutexes, so conditions have been the way to go.
I do accept criticism and improvements to this solution, I don't really know if it is correct/good practice.
Related
I have a question regarding threads in C, I know that to create a thread the function pthread_create is needed and I'm currently working on the dining philosopher problem and in this implementation of that problem I have to look if a philosopher has died of starvation.
I tested my programs and it works well, but to look if a philosopher has died I create another thread that'll always run and check in it if a philosoper has died.
a philosopher will die of starvation if he has not eat during a certain amount of time since his last meal.
Defining the general structure of the program and headers.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <sys/time.h>
struct t_phil;
typedef struct t_info t_info;
typedef struct t_phil t_phil;
typedef struct t_info
{
int num_of_phil;
t_phil *philo;
int min_dinner;
pthread_mutex_t *mutex;
int plate_eaten;
int num_of_dead_phil;
int time_to_die;
int time_to_sleep;
int time_to_eat;
pthread_mutex_t t_mut;
} t_info;
typedef struct t_phil
{
int number;
int has_eaten_all;
int finished_meal;
int is_dead;
t_info *data;
pthread_mutex_t *right;
pthread_mutex_t *left;
struct timeval last_dinner;
pthread_t thread;
} t_phil;
int count = 0;
Here is the function, that'll simulate the dinner, this one works as intended but I am open to possible error or improvement.
void *routine(void *args)
{
t_phil *philo = (t_phil *)(args);
int i = philo->number;
if ((philo->number % 2))
sleep(1);
gettimeofday(&philo->last_dinner, 0);
while (!philo->is_dead)
{
pthread_mutex_lock(philo->left);
printf("Philosopher : %i has take left fork\n", philo->number + 1);
pthread_mutex_lock(philo->right);
printf("Philosopher : %i has take right fork\n", philo->number + 1);
gettimeofday(&philo->last_dinner, 0);
printf("Philosopher :%i is eating in at %li\n", philo->number + 1, philo->last_dinner.tv_sec * 1000);
pthread_mutex_lock(&philo->data->t_mut);
// if (philo->data->num_of_dead_phil && !philo->data->min_dinner)
// break;
if (philo->is_dead)
break;
gettimeofday(&philo->last_dinner, NULL);
philo->finished_meal++;
if (philo->finished_meal == philo->data->min_dinner)
{
philo->data->plate_eaten++;
philo->has_eaten_all = 1;
}
sleep(philo->data->time_to_eat);
pthread_mutex_unlock(&philo->data->t_mut);
pthread_mutex_unlock(philo->left);
pthread_mutex_unlock(philo->right);
if (philo->has_eaten_all)
break;
printf("Philosopher : %i is now sleeping at %li\n", philo->number + 1, philo->last_dinner.tv_sec * 1000);
sleep(philo->data->time_to_sleep);
printf("Philosopher : %i is now thinking at %li\n", philo->number + 1, philo->last_dinner.tv_sec * 1000);
}
return (NULL);
}
This one function is the one not working as intended, and I don't know why is this happening right now, as my if statement seems to have the right condition but I am never entering in the if statement meaning that condition is never met while it should be.
I tested a lot of value and the same result happen each time
void *watchers_phil(void *args)
{
t_info *data = (t_info *)args;
t_phil *phil = data->philo;
int i = 0;
struct timeval now;
while (1)
{
if (data->plate_eaten == data->num_of_phil)
break;
while (i < data->num_of_phil)
{
if ((phil[i].last_dinner.tv_sec) >= ((phil[i].last_dinner.tv_sec) + (long int)data->time_to_die))
{
gettimeofday(&now, NULL);
printf("Unfortunately Philosopher : %i, is dead because of starvation at %li....", phil[i].number, (now.tv_sec * 1000));
phil[i].is_dead = 1;
}
i++;
}
i = 0;
}
return (NULL);
}
int main(int argc, char *argv[])
{
t_info data;
pthread_t watchers;
memset(&data, 0, sizeof(t_info));
data.num_of_phil = atoi(argv[1]);
data.min_dinner = atoi(argv[2]);
data.time_to_eat = atoi(argv[3]);
data.time_to_die = atoi(argv[4]);
data.time_to_sleep = atoi(argv[5]);
t_phil *philo = malloc(sizeof(t_phil) * data.num_of_phil);
if (!philo)
return (1);
pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t) * data.num_of_phil);
data.mutex = mutex;
if (!mutex)
{
free(philo);
return (1);
}
int i = 0;
while (i < data.num_of_phil)
{
pthread_mutex_init(&data.mutex[i], NULL);
i++;
}
printf("Number : %i\n", data.num_of_phil);
pthread_mutex_init(&data.t_mut, NULL);
i = 0;
while (i < data.num_of_phil)
{
philo[i].number = i;
philo[i].has_eaten_all = 0;
philo[i].data = &data;
philo[i].is_dead = 0;
philo[i].right = &data.mutex[i];
if (i == (data.num_of_phil - 1))
philo[i].left = &data.mutex[0];
else
philo[i].left = &data.mutex[i + 1];
i++;
}
data.philo = philo;
i = 0;
while (i < data.num_of_phil)
{
pthread_create(&data.philo[i].thread, NULL, routine, &data.philo[i]);
i++;
}
pthread_create(&watchers, NULL, watchers_phil, &data);
i = 0;
while (i < data.num_of_phil)
{
pthread_join(data.philo[i].thread, NULL);
i++;
}
pthread_join(watchers, NULL);
printf("Dinner eaten : %i\n", data.plate_eaten);
i = 0;
while (i < data.num_of_phil)
{
pthread_mutex_destroy(&data.mutex[i]);
i++;
}
pthread_mutex_destroy(&data.t_mut);
}
I recommend the following approach:
Have an array containing a timestamp for each philosopher thread.
Each philosopher thread updates its respective timestamp either on beginning or ending eating. To determine its own death it can use this timestamp as well. You could store the eating time or the time when starvation occurs, I personally would rather do the latter as it requires calculating this starvation time just once (and you don't need the eating time anywhere else) while the former would need to do it on every test for starvation.
The monitoring thread iterates over all these timestamps doing the following:
Determine if a thread has starved (current time – just get it once in front of the loop! – being after timestamp stored, if you follow my recommendation above). Flag the thread as starved by some suitable way so that you can ignore it next time.
From non-starved threads remember the minimum value.
After iteration sleep as long until this minimum time will be reached (maybe waking up minimally earlier for better precision). If a philosopher updates the timestamp corresponding to this minimum – never mind, the monitor will wake up in vain, just doing nothing, but that won't hurt apart from consuming a bit of CPU time.
Updating the timestamps: You need to be aware that – unless you can guarantee atomic timestamp update – you need to protect them against race conditions between philosopher threads and monitoring thread.
I see two options for:
One single mutex for the entire array. Easy to implement, but either of the involved threads might block another one (two philosophers, if both trying to update at the same time, a philosopher the monitor or the monitor one or more philosophers). If one thread is preempted right while holding this mutex the blocked one(s) need(s) to wait until this thread is scheduled again.
One mutex for each timestamp. The advantage is that blocking only can occur between the monitor and one single philosopher while all other philosophers can go on as usual. The implementation is more complex, though, and the monitor thread will run its tests a bit slower (you won't notice...) due to having to lock and unlock mutexes again and again (system calls involved, these are rather expensive).
Philosophers should hold the mutex over the entire time between reading the timestamp for checking own starvation until having written the updated starvation time. This assures the philosopher cannot starve right while eating, though in between you should do as little work as possible, especially if you opt for one single mutex (if you cannot avoid, then rather opt for multiple ones).
I'm currently playing around with the POSIX library and trying out conditional variables.
At the moment I'm using a queue to scheduele tasks, if there is a task one thread uses pthread_cond_signal to wake up a thread that is waiting at pthread_cond_wait.
However there might be a point where no new tasks are created, so every thread is waiting at pthread_cond_wait and the programm is stuck there.
Is there anyway for me noticing if all my threads are waiting at pthread_cond_wait? I've tried using a counter and a leave variable but I did not get it working.
My code looks simliar to this code here: https://code-vault.net/lesson/j62v2novkv:1609958966824
,except each thread is adding new tasks (in executeTask) instead of only the main method adding them.
EDIT:
Here is my version of the code from the website above (which is loading for me:/)
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#define THREAD_NUM 4
typedef struct Task
{
int a, b;
} Task;
Task taskQueue[256];
int taskCount = 0;
// THIS VARIABLE IS ADDED
int moreTasks = 10;
pthread_mutex_t mutexQueue;
pthread_cond_t condQueue;
void executeTask(Task *task)
{
usleep(50000);
int result = task->a + task->b;
printf("The sum of %d and %d is %d\n", task->a, task->b, result);
// THIS PART IS ADDED
if (moreTasks > 0)
{
pthread_mutex_lock(&mutexQueue);
moreTasks--;
pthread_mutex_unlock(&mutexQueue);
Task t = {
.a = rand() % 100,
.b = rand() % 100};
submitTask(t);
}
}
void submitTask(Task task)
{
pthread_mutex_lock(&mutexQueue);
taskQueue[taskCount] = task;
taskCount++;
pthread_mutex_unlock(&mutexQueue);
pthread_cond_signal(&condQueue);
}
void *startThread(void *args)
{
while (1)
{
Task task;
pthread_mutex_lock(&mutexQueue);
while (taskCount == 0)
{
pthread_cond_wait(&condQueue, &mutexQueue);
}
task = taskQueue[0];
int i;
for (i = 0; i < taskCount - 1; i++)
{
taskQueue[i] = taskQueue[i + 1];
}
taskCount--;
pthread_mutex_unlock(&mutexQueue);
executeTask(&task);
}
}
int main(int argc, char *argv[])
{
pthread_t th[THREAD_NUM];
pthread_mutex_init(&mutexQueue, NULL);
pthread_cond_init(&condQueue, NULL);
int i;
for (i = 0; i < THREAD_NUM; i++)
{
if (pthread_create(&th[i], NULL, &startThread, NULL) != 0)
{
perror("Failed to create the thread");
}
}
srand(time(NULL));
for (i = 0; i < 100; i++)
{
Task t = {
.a = rand() % 100,
.b = rand() % 100};
submitTask(t);
}
for (i = 0; i < THREAD_NUM; i++)
{
if (pthread_join(th[i], NULL) != 0)
{
perror("Failed to join the thread");
}
}
pthread_mutex_destroy(&mutexQueue);
pthread_cond_destroy(&condQueue);
return 0;
}
My Probleme now is that after a while the taskCount is always zero and tehrefore all my threads are at pthread_cond_wait(&condQueue, &mutexQueue); in the startThread methods.
Is ther any way of me noticing if all threads are at this place? As statet above if already tried using a counter for the threads that are currently waiting (and also a version where I counted the threads that are working) but I could not figure out how to stop all the threads once every task is done.
If you want to know how many threads are waiting on a condition var, just increase a global counter before you go into wait mode and decrease it on wake up. With that you'll know how many threads are currently waiting.
e.g.
//global scope
int num_waiting_threads = 0;
//in function startThread
while (taskCount == 0)
{
//if num_waiting_threads == THREAD_NUM, all threads are waiting
++num_waiting_threads;
pthread_cond_wait(&condQueue, &mutexQueue);
--num_waiting_threads;
}
Since you have a fixed number of tasks, after processing every task, every thread will be sooner or later just waiting around for newly submitted tasks. Your execute function will only add in total 10 new tasks (moreTasks is initialized with 10 and decreased continuously and if that counter hits zero, no more tasks will be submitted).
Therefore, you could/should use pthread_cond_timedwait and wake up by yourself to check the num_waiting_threads state. If all threads are waiting, break out of the loop and exit the thread.
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <pthread.h>
#include <math.h>
#include <stdbool.h>
#include <string.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
char mutual[100];
int firstWrote = 0;
int secondWrote = 0;
int firstFinished = 0;
int secondFinished = 0;
void* reader(void* arg)
{
FILE* file = fopen((const char*)arg,"r");
if(file == NULL)
{
printf("Problem with opening file %s",(const char*)arg);
exit(4);
}
else
{
char which[25];
while(fgets(mutual,100,file))
{
pthread_mutex_lock(&mutex);
strcpy(which,(const char*)arg);
if(strcmp(which,"/etc/profile")==0)
{
firstWrote = 1;
//printf("%s\n",mutual);
fflush(stdout);
}
if(strcmp(which,"/etc/passwd")==0)
{
secondWrote = 1;
//printf("%s\n",mutual);
fflush(stdout);
}
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
if(strcmp(which,"/etc/profile")==0)
{
firstWrote = 0;
firstFinished= 1;
}
else if(strcmp(which,"etc/passwd")==0)
{
secondWrote = 0;
secondFinished = 1;
}
}
fclose(file);
pthread_exit(NULL);
}
int main(void)
{
FILE* pawel1 = fopen("PAWEL1","w");
FILE* pawel2 = fopen("PAWEL2","w");
if(pawel1 == NULL || pawel2 == NULL)
{
printf("Problem with opening file");
return 1;
}
pthread_t p1,p2;
pthread_attr_t attr;
if(pthread_attr_init(&attr) != 0)
exit(2);
if(pthread_attr_setd2etachstate(&attr,PTHREAD_CREATE_DETACHED))
exit(3);
pthread_create(&p1,&attr,reader,(void*)"/etc/profile");
pthread_create(&p2,&attr,reader,(void*)"/etc/passwd");
while(1)
{
pthread_mutex_lock(&mutex);
if(firstFinished == 1 && secondFinished == 1)
break;
pthread_cond_wait(&cond,&mutex);
if(firstWrote == 1)
{
fprintf(pawel1,"%s",mutual);
firstWrote = 0;
}
if(secondWrote == 1)
{
fprintf(pawel2,"%s",mutual);
secondWrote = 0;
}
pthread_mutex_unlock(&mutex);
}
fclose(pawel1);
fclose(pawel2);
pthread_attr_destroy(&attr);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
The app task is to made two different thread read from two files (/etc/passwd and /etc/profile) to a global buffer, and print results to proper file (PAWEL1 or PAWEL2) in main function , and it seems that i handle this behaviour with global flags but what i get is all lines from passwd and profile in one file - PAWEL2, and the file PAWEL1 is left empty.
Since you share a one-line buffer among all threads, including both readers, you must ensure that the main thread runs after each reader thread, never one reader immediately after the other (or itself). Otherwise, one reader can overwrite the data and status flags set by the other. You can accomplish this by
using the existing state flags to determine whose turn it is to run, and
broadcasting to the CV instead of merely signalling it, so that all participating threads get an opportunity to evaluate whether it is appropriate for them to perform a unit of work.
In particular, the rules could look like this:
Readers can run if !firstWrote && !secondWrote, which shows that the buffer is available for new data. They set the appropriate one of those variables after successfully reading the line, thus blocking both readers from overwriting the buffer until its contents are consumed, or they set the appropriate Finished variable and terminate if they cannot read a line.
The main thread can run if firstWrote || secondWrote || (firstFinished && secondFinished). After successfully reading out the buffer, it is responsible for resetting the appropriate one of firstWrote and secondWrote to zero.
When I say a thread "can run", I am talking about the condition associated with each thread's CV wait. The proper idiom for CV usage always involves such a condition, and it looks like this (pseudocode):
lock_mutex()
// ... maybe other code ...
while (condition_is_false()) {
wait_on_cv()
}
// ... maybe other code ...
unlock_mutex()
All accesses to the data that contribute to the condition must be performed under protection from the mutex.
The loop around the wait accounts for two main things:
Control arriving at the condition check when the thread is already clear to proceed
Waking from the wait despite not being clear to proceed, either because of receiving a signal or spuriously (which occasionally does happen)
You can add another level of looping around that (and that would be appropriate for your uses).
Note also that you could simplify your logic considerably by using one set of *Wrote and *Finished variables instead of a separate set for each reader. You would then achieve the necessary distinction among program states by using more values for those variables than just 0 and 1.
I have written a program which simulates the producer consumer problem and I am running into a couple of issues. This was written using Win32 API.
I am using two semaphores full and empty to perform the counting for the buffer where items are stored. There is a mutex as well to control access to the critical section. I have two functions: one creates an item based on a simple calculation: threads * 1000000 + count, while the other consumes it.
The buffer has 10 spaces in it however the program should hopefully be able to work for different number of spaces and threads. The Producer thread goes through the buffer then starts over until the semaphore counts up to 10. I am running into two problems that I can't seem to find a solution too nor do I get many details in the debugger.
1) The commented part which has the printf() function crashes the thread every single time. Nothing ever gets printed to console. I have tried using just a string as well with no other variables being outputted. No success. I found documentation that printf() can run into trouble when used in a multithreaded environment however in this case it is within the critical section and it crashes on the first try. How can I print that information to console?
2) When I remove the print statements and run the code the threads still crash at different points every time. I can't figure out why. The producer thread is supposed to wait for the empty semaphore and the mutex, put the item in, then increase the count for the full semaphore (signifying an item has been added). When it reaches 10 it should stop. The Consumer thread waits for the full semaphore and the mutex, removes an item, then increases the count for the empty semaphore. Is there something in the way I've written it that is causing these thread exits?
This is what I get:
The program '[5348] OperatingSystem.exe' has exited with code 0 (0x0).
#include<stdio.h>
#include<windows.h>
#include<ctype.h>
#include<tchar.h>
#include<strsafe.h>
#include<conio.h>
#include<time.h>
#define threads 1
#define MAX 10
typedef struct objects {
HANDLE empty;
HANDLE full;
HANDLE mutex;
int buffer[10];
};
struct objects data;
DWORD WINAPI Producers(LPVOID lpParam){
int count = 0;
int item;
while (TRUE){
if (data.buffer[count] == 0){
WaitForSingleObject(data.empty, INFINITE);
WaitForSingleObject(data.mutex, INFINITE);
item = threads * 1000000 + count;
data.buffer[count] = item;
//printf("Producer has produced: %d", item);
ReleaseMutex(data.mutex);
ReleaseSemaphore(data.full, 1, NULL);
}
count++;
if (count == 10){ count = 0; }
};
}
DWORD WINAPI Consumers(LPVOID lpParam){
int count = 0;
while (TRUE){
if (data.buffer[count] != 0){
WaitForSingleObject(data.full, INFINITE);
WaitForSingleObject(data.mutex, INFINITE);
//printf("Consumer has consummed: %d", data.buffer[count]);
data.buffer[count] = 0;
ReleaseMutex(data.mutex);
ReleaseSemaphore(data.empty, 1, NULL);
}
count++;
if (count == 10){ count = 0; }
};
}
int main(int argc, char *argv[]) {
struct objects data;
LONG initialCount = 0;
LONG maxCount = 10;
data.empty = CreateSemaphore(NULL, maxCount, maxCount, NULL);
data.full = CreateSemaphore(NULL, initialCount, maxCount, NULL);
data.mutex = CreateMutex(NULL, FALSE, NULL);
DWORD ConsumerId[threads];
HANDLE ConsumerThread[threads];
DWORD ProducerId[threads];
HANDLE ProducerThread[threads];
for (int i = 0; i < threads; i++){
ProducerThread[i] = CreateThread(
NULL,
0,
Producers,
NULL,
0,
&ProducerId[i]);
ConsumerThread[i] = CreateThread(
NULL,
0,
Consumers,
NULL,
0,
&ConsumerId[i]);
}
}
I was trying to learn something about parallel programming, so I tried to implement Peterson's algorithm for an easy example where one shared counter is incremented by 2 threads. I know that Peterson's isn't optimal due to busy waiting but I tried it only for study reasons.
I supposed that the critical section of this code is in the thread function add where the shared counter is incremented. So i call the enter_section function before the counter incrementation and after it I called the leave_function. Is this part wrong? Did I asses the critical section wrong? Problem is that the counter sometimes gives an unexpectable value when these 2 threads are done. It has to be a synchronization problem between threads but I just don't see it... Thanks for any help.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
int counter; /* global shared counter */
int flag[2] = {0, 0}; /* Variables for Peterson's algorithm */
int turn = 0;
typedef struct threadArgs
{
pthread_t thr_ID;
int num_of_repeats;
int id;
} THREADARGS;
void enter_section (int thread) {
int other = 1 - thread;
flag[thread] = 1;
turn = thread;
while ((turn == thread) && (flag[other] == 1));
return;
}
void leave_section (int thread) {
flag[thread] = 0;
return;
}
void * add (void * arg) {
int i;
THREADARGS * a = (THREADARGS *) arg;
for (i = 0; i < a->num_of_repeats; i++) {
enter_section(a->id);
counter++;
leave_section(a->id);
}
return NULL;
}
int main () {
int i = 1;
pthread_attr_t thrAttr;
THREADARGS threadargs_array[2];
pthread_attr_init (&thrAttr);
pthread_attr_setdetachstate (&thrAttr, PTHREAD_CREATE_JOINABLE);
/* creating 1st thread */
threadargs_array[0].id = 0;
threadargs_array[0].num_of_repeats = 1000000;
pthread_create(&threadargs_array[0].thr_ID, &thrAttr, add, &threadargs_array[0]);
/* creating 2nd thread */
threadargs_array[1].id = 1;
threadargs_array[1].num_of_repeats = 2000000;
pthread_create(&threadargs_array[1].thr_ID, &thrAttr, add, &threadargs_array[1]);
/* free resources for thread attributes */
pthread_attr_destroy (&thrAttr);
/* waiting for 1st thread */
pthread_join (threadargs_array[0].thr_ID, NULL);
printf("First thread is done.\n");
/* waiting for 2nd thread */
pthread_join (threadargs_array[1].thr_ID, NULL);
printf("Second thread is done.\n");
printf("Counter value is: %d \n", counter);
return (EXIT_SUCCESS);
}
You have several problems here:
the access to your variables will we subject to optimization, so you'd have to declare them volatile, at least.
Algorithms like this that access data between threads without any of the lock data structures that are provided by POSIX can only work if your primitive operations are guaranteed to be atomic, which they usually aren't on modern processors. In particular the ++ operator is not atomic.
There would be several ways around this, in particular the new C standard C11 offers atomic primitives. But if this is really meant for you as a start to learn parallel programming, I'd strongly suggest that you first look into mutexes, condition variables etc, to learn how POSIX is intended to work.