-1

I'm doing a C application that reads and parses data from a set of sensors and, according to the readings of the senors, it turns on or off actuators.

For my application I will be using two threads, one to read and parse the data from the sensors and another one to act on the actuators. Obviously we may face the problem of one thread reading data from a certain variable while another one is trying to write on it. This is a sample code.

#include <pthread.h>
int sensor_values;
void* reads_from_sensor(){
//writes on sensor_values, while(1) loop
}
void* turns_on_or_off(){
//reads from sensor_values, while(1) loop
}

int main(){
pthread_t threads[2];
pthread_create(&threads[1],NULL,reads_from_sensor,NULL);
pthread_create(&threads[2],NULL,turns_on_or_off,NULL);
//code continues after
}

My question is how I can solve this issue, of a certain thread writing on a certain global variable while other thread is trying to read from it, at the same time. Thanks in advance.

  • 2
    This is a standard problem of thread synchronization. Just google exactly that, and specifically for *pthread* if you want to learn how to do it with them. As it stands your question is too broad to answer on SO. But a simple and easy to implement mechanism is using a mutex variable and `pthread_mutex_lock()`, `pthread_mutex_unlock()` pairs. – Iharob Al Asimi Mar 18 '18 at 16:29
  • what is the `while ( 1 ) ` loop for – 0.sh Mar 18 '18 at 16:38
  • Which variable is the common resource for your thread and what is the function of it, i.e. what are the threads going to do with the shared resource? Depending on that you might have to use different strategies from mutex, semaphores or condition variables. Unless you specify your design in more detail, this question is just to broad and we cannot give you an specific answer. – Pablo Mar 18 '18 at 16:46
  • And doing `while(1)` in a loop in a thread is a bad idea, you should be able to tell a thread to stop working instead of letting it go in an endless loop. – Pablo Mar 18 '18 at 16:48
  • The project is still in an alpha stage. I'll make sure I optimize it once it is done. @Pablo, the shared variable is sensor_values. reads_from_sensors write on it and turns_on_or_off reads from it. – Mário Marques Mar 18 '18 at 16:51
  • and what is `sensor_value`? an `int`, `double`, `float` or a complex data structure? – Pablo Mar 18 '18 at 16:52
  • Sensor_value would be a float as it stores a value measured by a certain sensor. That value can either be voltage, temperature or humidity. – Mário Marques Mar 18 '18 at 16:54
  • In such a case you can use condition variables, see my answer for more details. – Pablo Mar 18 '18 at 18:03

2 Answers2

0

OP wrote in the comments

The project is still in an alpha stage. I'll make sure I optimize it once it is done. @Pablo, the shared variable is sensor_values. reads_from_sensors write on it and turns_on_or_off reads from it.

...

sensor_value would be a float as it stores a value measured by a certain sensor. That value can either be voltage, temperature or humidity

In that case I'd use conditional variables using pthread_cond_wait and pthread_cond_signal. With these functions you can synchronize threads with each other.

The idea is that both threads get a pointer to a mutx, the condition variable and the shared resource, whether you declared them a global or you pass them as thread arguments, doesn't change the idea. In the code below I'm passing all of these as thread arguments, because I don't like global variables.

The reading thread would lock the mutex and when it reads a new value of the sensor, it writes the new value in the shared resource. Then it call pthread_cond_signal to send a signal to the turning thread that a new value arrived and that it can read from it.

The turning thread would also lock the mutex and execute pthread_cond_wait to wait on the signal. The locking must be done in that way, because pthread_cond_wait will release the lock and make the thread block until the signal is sent:

man pthread_cond_wait

DESCRIPTION

The pthread_cond_timedwait() and pthread_cond_wait() functions shall block on a condition variable. The application shall ensure that these functions are called with mutex locked by the calling thread; otherwise, an error (for PTHREAD_MUTEX_ERRORCHECK and robust mutexes) or undefined behavior (for other mutexes) results.

These functions atomically release mutex and cause the calling thread to block on the condition variable cond; atomically here means atomically with respect to access by another thread to the mutex and then the condition variable. That is, if another thread is able to acquire the mutex after the about-to-block thread has released it, then a subsequent call to pthread_cond_broadcast() or pthread_cond_signal() in that thread shall behave as if it were issued after the about-to-block thread has blocked.

Example:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

struct thdata {
    pthread_mutex_t *mutex;
    pthread_cond_t *cond;
    int *run;
    float *sensor_value; // the shared resource
};

void *reads_from_sensors(void *tdata)
{
    struct thdata *data = tdata;

    int i = 0;

    while(*data->run)
    {
        pthread_mutex_lock(data->mutex);

        // read from sensor
        *data->sensor_value = (rand() % 2000 - 1000) / 10.0;

        // just for testing, send a singnal only every
        // 3 reads
        if((++i % 3) == 0)
        {
            printf("read: value == %f, sending signal\n", *data->sensor_value);
            pthread_cond_signal(data->cond);
        }

        pthread_mutex_unlock(data->mutex);

        sleep(1);
    }

    // sending signal so that other thread can
    // exit
    pthread_mutex_lock(data->mutex);
    pthread_cond_signal(data->cond);
    pthread_mutex_unlock(data->mutex);

    puts("read: bye");
    pthread_exit(NULL);
}

void *turns_on_or_off (void *tdata)
{
    struct thdata *data = tdata;


    while(*data->run)
    {
        pthread_mutex_lock(data->mutex);

        pthread_cond_wait(data->cond, data->mutex);

        printf("turns: value read: %f\n\n", *data->sensor_value);

        pthread_mutex_unlock(data->mutex);

        usleep(1000);
    }

    puts("turns: bye");
    pthread_exit(NULL);
}

int main(void)
{

    srand(time(NULL));

    struct thdata thd[2];
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

    // controlling vars
    int run_rfs = 1;
    int run_tof = 1;

    float sensor_value;

    thd[0].run = &run_rfs;
    thd[1].run = &run_tof;

    thd[0].mutex = &mutex;
    thd[1].mutex = &mutex;

    thd[0].cond = &cond;
    thd[1].cond = &cond;

    thd[0].sensor_value = &sensor_value;
    thd[1].sensor_value = &sensor_value;


    pthread_t th[2];

    printf("Press ENTER to exit...\n");

    pthread_create(th, NULL, reads_from_sensors, thd);
    pthread_create(th + 1, NULL, turns_on_or_off, thd + 1);

    getchar();

    puts("Stopping threads...");

    run_rfs = 0;
    run_tof = 0;

    pthread_join(th[0], NULL);
    pthread_join(th[1], NULL);


    return 0;
}

Output:

$ ./a 
Press ENTER to exit...
read: value == -99.500000, sending signal
turns: value read: -99.500000

read: value == -25.200001, sending signal
turns: value read: -25.200001

read: value == 53.799999, sending signal
turns: value read: 53.799999

read: value == 20.400000, sending signal
turns: value read: 20.400000


Stopping threads...
read: bye
turns: value read: 20.400000

turns: bye

Note that in the example I only send the signal every 3 seconds (and do a long sleep(1)) for testing purposes, otherwise the terminal would overflow immediately and you would have a hard time reading the output.

See also: understanding of pthread_cond_wait() and pthread_cond_signal()

Pablo
  • 13,271
  • 4
  • 39
  • 59
0

Your question is too generic. There are different multithread synchronization methods mutex, reader-writer locks, conditional variables and so on.

The easiest and most simple are mutex (mutual excluasion). They are pthread_mutex_t type variables. You first need to initialize them; you can do it in two ways:

  • assigning to the mutex variable the constant value PTHREAD_MUTEX_INITIALIZER
  • calling the funtion pthread_mutex_init

Then before reading or writing a shared variable you call the function int pthread_mutex_lock(pthread_mutex_t *mutex); and after exited the critical section you must release the critical section by calling int pthread_mutex_unlock(pthread_mutex_t *mutex);.

If the resource is busy the lock will block the execution of your code until it gets released. If you want to avoid that take a look at int pthread_mutex_trylock(pthread_mutex_t *mutex);.

If your program has much more reads than writes on the same shared variable, take a look at the Reader-Writer locks.

roschach
  • 8,390
  • 14
  • 74
  • 124