Ищу пример использования потоков мьютекса

#c #multithreading #pthreads #mutex

#c #многопоточность #pthreads #мьютекс

Вопрос:

Я учусь pthreads и mutex variables , в частности. Я смотрю на пример кода, который вы можете найти здесь. Он использует потоки для вычисления скалярного произведения. Центральной частью кода является функция точечного произведения:

  void *dotprod(void *arg)
 {


    int i, start, end, len ;
    long offset;
    double mysum, *x, *y;
    offset = (long)arg;
     
    len = dotstr.veclen;
    start = offset*len;
    end   = start   len;
    x = dotstr.a;
    y = dotstr.b;


    mysum = 0;
    for (i=start; i<end ; i  ) 
     {
       mysum  = (x[i] * y[i]);
     }

    /*
    Lock a mutex prior to updating the value in the shared
    structure, and unlock it upon updating.
    */
    pthread_mutex_lock (amp;mutexsum);
    dotstr.sum  = mysum;
    pthread_mutex_unlock (amp;mutexsum);

    pthread_exit((void*) 0);
 }
  

Первый вопрос заключается в том, действительно ли блокировка потоков здесь необходима. Я попробовал удалить вызовы блокировки и разблокировки, и конечный результат был тем же самым. Но это всего лишь пример, поэтому, возможно, на практике блокировки не нужны. Что подводит меня к моему второму вопросу: я подумал, что попытаюсь изменить код на что-то, результат чего может измениться в зависимости от того, обращаются ли несколько потоков dotstr.sum . Я снова избавился от замка и попробовал:

 dotstr.sum  = mysum;
dotstr.sum *= 1.1;
  

Но, вопреки моим ожиданиям, это не привело к возникновению условия гонки. Результат был точно таким же в нескольких испытаниях. Почему это должно быть? Наконец, может ли кто-нибудь предложить простое изменение кода, в котором потребуется блокировка?

Комментарии:

1. Несинхронизированный доступ приводит к неопределенному поведению. Вы не можете ожидать какого-либо конкретного поведения для какого-либо конкретного запуска (например, мгновенного прерывания каждый раз).

2. «условие гонки» не означает, что потоки действительно мешали друг другу. Это означает, что потоки могут мешать друг другу.

3. Просто в качестве примечания: строка offset = (long)arg; не будет работать на некоторых платформах, таких как Microsoft Windows, где sizeof(long) == 4 и sizeof(void*) == 8 . Возможно, вы захотите рассмотреть возможность использования uintptr_t вместо long , поскольку этот тип был определен стандартом специально для хранения того же количества битов, что и указатель.

4. Всегда существует вероятность того, что один поток будет выгружен (т. Е. Остановлен) в критической ситуации, так что другой поток выиграет гонку. Если вы хотите воспроизвести эту ситуацию, то вы можете добавить случайное поведение в более быстрый поток, например if (rand()0==0) DoSomethingWhichCostsALotOfPerformance();

5. Посмотрите здесь .