Реализация MVar в C? - PullRequest
       7

Реализация MVar в C?

11 голосов
/ 18 января 2012

Есть ли известная реализация Haskell MVar в C?Существует пример о том, как реализовать его в C ++.Но я хотел бы реализовать это на C - скажем, на данный момент только MVar CInt эквивалент в C.Написание примитивов синхронизации может быть сложным.Поэтому я хотел бы избежать дублирования усилий, если кто-то уже сделал это.Я недостаточно хорошо понимал приведенный выше пример C ++, чтобы уверенно перевести его на C - он очень хорошо скрывает алгоритмические детали от моего C ++ - неопытного ума:)

Причина, по которой я думаю о написании MVar на C, заключается в том, чтопотому что для меня действительно легко использовать привязку FFI к внешней библиотеке C, чтобы получить поток данных, и использовать потоки Haskell для получения данных (из хранимых векторов, чтобы избежать маршалинга данных - MVar CInt здесь хранит, сколькоСохраняемые векторы были заполнены).Мне нужно убедиться, что потоки C, записывающие в сохраняемые местоположения, заблокированы, пока поток Haskell читает данные.Вот где помогает синхронизация MVar на стороне C.Также гораздо быстрее вызывать небезопасную или даже безопасную функцию C из Haskell (~ 15 нс для небезопасного, ~ 150 нс для безопасного в моем тесте), чем обратный вызов в Haskell из C (~ 5us).Если бы обратные вызовы были быстрыми, я бы вместо этого вызвал бы функцию C обратно в Haskell и заблокировал бы на Haskell MVar.

Обновление:

Подойдет алгоритм в псевдокодетакже.Это должно быть довольно легко реализовать в C, учитывая алгоритм для newEmptyMVar, takeMVar и putMVar.

Ответы [ 2 ]

4 голосов
/ 20 января 2012

MVar может быть реализовано в C с использованием такой структуры, как показано ниже:

typedef struct{
  pthread_cond_t put_cond;
  pthread_cond_t take_cond;
  pthread_mutex_t lock;
  void* value;
} mvar;

put_cond используется потоками, которые помещают значения в MVar, чтобы сигнализировать другим потокам, которые ожидают получения значения из MVar.take_cond является аналогом для дубля.Что касается планирования, то это планирование по умолчанию.

value - указатель void - поэтому приведенная выше структура может использоваться для защиты любого типа значения в MVar - конечно, C позволит вам записать этоуказатель вне MVar - так что, программа должна гарантировать, что этого не произойдет (избегая расщепления value указатель вне MVar - всегда обращайтесь к нему через функции MVar).

Инициализация MVar:

mvar* newMVar(void* arg){
 //create new mvar struct
 mvar* var=(mvar*) malloc(sizeof(mvar));
 pthread_mutex_init(&var->lock,NULL);
 pthread_cond_init(&var->take_cond,NULL);
 pthread_cond_init(&var->put_cond,NULL);
 var->value = arg;
 return (mvar*) var;
}

Пусто MVar - использует вышеуказанную функцию:

mvar* newEmptyMVar(){
 return newMVar(NULL);
}

putMVar:

void putMVar(mvar* var,void* value){
  pthread_mutex_lock(&var->lock);
  while(var->value != NULL)
    pthread_cond_wait(&var->put_cond,&var->lock);//if MVar is full, wait until another thread takes the value - release the mutex,  and wait on put_cond to become true
  var->value = value;//if here, we got the signal from another thread that took MVar - MVar is empty now. OK to fill
  pthread_cond_signal(&var->take_cond);//signal other threads that value is available for taking now
  pthread_mutex_unlock(&var->lock);
}

takeMVar:

void* takeMVar(mvar* var){
  void* value;
  pthread_mutex_lock(&var->lock);
  while(var->value == NULL)
    pthread_cond_wait(&var->take_cond,&var->lock);//if MVar is empty, wait until another thread fills it - release the mutex, and   wait on take_cond to become true
  //take the value
  value = var->value;
  var->value = NULL; //push NULL value to indicate MVar is empty now
  pthread_cond_signal(&var->put_cond);//signal other threads that value is available for filling now
  pthread_mutex_unlock(&var->lock);
  return value; //return the value that was taken from MVar
}

Полный код на github , с примером , который показывает, как использовать MVar.

MVar довольно быстрый, если к нему обращается только один поток (и тяжелыйутверждение).Но в условиях сильной конкуренции и множественных потоков (даже двух) масштабируется очень плохо.Это не удивительно, потому что pthreads работает.Я обнаружил, что MVar в Haskell очень хорошо работает с несколькими потоками.Это не удивительно, учитывая, насколько хорошо в GHC реализованы легкие потоки и примитивы параллелизма.

0 голосов
/ 18 января 2012

Код в примере не очень специфичен для C ++. Основными битами являются именно pthread -фрагменты.

...