Как мне синхронизировать потоки без использования pthread_join ()? - PullRequest
2 голосов
/ 15 июня 2019

Я создал простую программу на С ++, которая используется для регистрации. Я создаю потоки в цикле for, который выполняется 1000 раз в test.cpp, который является файлом драйвера. pthread_create вызывает функцию печати в tt.cpp, которая записывает входной параметр, т.е. i, в file.txt. Я хочу синхронизировать темы.

Я пытался использовать pthread_join, который синхронизировал потоки, но я хочу синхронизировать потоки без использования объединения.

Я знаю, что если поток заблокирован, многие потоки будут ожидать, пока он не будет разблокирован, и после того, как этот поток разблокирован, любой из ожидающих потоков заблокирует функцию. Итак, я попытался использовать статическую целочисленную переменную в tt.cpp и попытался сравнить ее с входным параметром, чтобы я мог использовать pthread_cond_wait и pthread_wait_signal, но у меня возникла ошибка сегментации при сравнении.

 /* if(j == *((int *)input)))
    This comparison gave me a segmentation fault */


-------------------------test.cpp---------------------------------
#include "thr.h"


pthread_mutex_t loc;
FILE *thePrintFile = NULL;

int main()
{
    pthread_mutex_init(&loc,NULL);

    pthread_t p;
    thePrintFile = fopen("file.txt","r+");
    for(int i =0; i<1000;i++)
    {
        pthread_create(&p,NULL,print,(void *)i);
    }
    for(int k = 0; k<100000;k++);
    /* i have used it to ensure that the main thread 
       doesn't exit before the pthreads finish writing */
    return 0;

}

    ------------------------tt.cpp------------------------------------
    #include "thr.h"

    extern pthread_mutex_t loc;
    extern FILE *thePrintFile;

    void* print(void *input)
    {
        if(pthread_mutex_trylock(&loc) == 0)
        {   

            fprintf(thePrintFile,"%s%d\n",(int *)input);
            fflush(thePrintFile);
            pthread_mutex_unlock(&loc);
        }   
    }

    -----------------------------thr.h--------------------------------
    #ifndef THR_H_INCLUDED
    #define THR_H_INCLUDED

    void* print(void *input);

    #endif

Данный ниже является частью file.txt.

1
5
9
10
11
12
13
14
15
16
18
19
20
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
21
201
202

Ответы [ 2 ]

1 голос
/ 15 июня 2019

Для правильной синхронизации потоков необходимо использовать устройство синхронизации, например, условную переменную . Поскольку вы используете C ++, вам гораздо лучше использовать его встроенные потоки , а не сырой pthread API.

Например, этот код синхронизирует 1000 потоков, так что каждый из них печатает свой собственный идентификатор, используя переменную условия и пару мьютексов, чтобы гарантировать, что идентификаторы печатаются по порядку, как отслеживается отдельной переменной, общей для всех. Это не очень эффективно, потому что все потоки борются за один и тот же мьютекс, но работает правильно. Это можно сделать более эффективным, создав переменную условия для каждого потока, но правильное выполнение этого потребовало бы лучшего понимания вашего фактического варианта использования.

#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <vector>

static std::mutex mutex;
static std::condition_variable condvar;
static int whos_next;

static void work(int id) {
  std::unique_lock<std::mutex> lock{mutex};
  // wait for our turn
  condvar.wait(lock, [=]() { return whos_next == id; });
  // it's our turn now - print our thread ID
  std::cout << id << '\n';
  ++whos_next;
  condvar.notify_all();    // notify the next thread to run
}

int main() {
  std::vector<std::thread> threads;
  for (int i = 0; i < 1000; i++)
    threads.push_back(std::thread([=]() { work(i); }));

  for (auto &t: threads)
    t.join();
}

Обратите внимание, что вышеприведенный код использует join не для синхронизации потоков друг с другом (они синхронизируются между собой с помощью мьютекса / условия), а для их предназначения: дождаться завершения потоков перед выходом main(). C ++ даже требует, чтобы вы сделали это перед уничтожением объекта std::thread, поэтому удаление объединения приводит к завершению программы. Вы можете легко доказать, что потоки не используют join для синхронизации, например, вставляя спящий перед соединениями, или соединяя их в обратном порядке.

1 голос
/ 15 июня 2019

Я вижу несколько проблем в вашем коде.

  • Вы показываете, что вы получаете ошибку сегментации в определенной строке.Эта строка отсутствует в фактическом коде, который вы опубликовали.
  • У вас есть тип возврата void * для вашей секции печати, но вы ничего не возвращаете.
  • Вы приводите int к void *.Размер указателя может не совпадать с размером int.Вместо этого измените аргумент функции печати на int.Тогда приведение не требуется.
  • Нельзя одновременно записывать 1000 потоков в один и тот же файл.Так что в этом случае нет причин создавать потоки, потому что вы все равно должны выполнять их последовательно.Но если вы это сделаете, то вы должны присоединиться к ним.

Тогда давайте посмотрим на сравнение, которое вы пытаетесь провести:

if(j == *((int *)input)))

j, очевидно, int.ввод недействителен *.На входе базы был int.Поскольку приведение int к void * не гарантируется безопасным, приведение обратно также небезопасно.Представьте, что int - это 64 бита, а указатель - 32 бита.Если вы бросите свой int в void *, вы потеряете 32 бита.Затем вы преобразуете данные из 32-битных в 64-битные, поэтому вы добавляете 32-битные 0.Очевидно, это вызовет проблемы.

Рассмотрите возможность использования C ++ 11, что значительно улучшит ваш код.https://solarianprogrammer.com/2011/12/16/cpp-11-thread-tutorial/

...