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

Есть ли известная реализация 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 (~5 мкс). Если бы обратные вызовы были быстрыми, я бы вместо этого вызвал функцию C обратно в Haskell и заблокировал бы Haskell MVar.

Обновление:

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


person Sal    schedule 18.01.2012    source источник
comment
Для тех, кто не знаком с Haskell MVar, см.: Control.Concurrent.MVar   -  person Dan Burton    schedule 18.01.2012
comment
Я не могу помочь вам с вашим вопросом, но откуда у вас время для внешних вызовов между Haskell и C. Вчера я блуждал, как сравнить этот критерий.   -  person jmg    schedule 18.01.2012
comment
@jmg, код здесь (для haskell-›C): hpaste.org/56609. Там нет критерия бенчмаркинга, но это должно быть довольно просто сделать, и результат должен быть довольно близким. Для обратного вызова C->Haskell см. код в этом сообщении: stackoverflow.com/questions/8902568/   -  person Sal    schedule 18.01.2012


Ответы (2)


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 является аналогом take. Что касается расписания, то это расписание по умолчанию.

value является пустым указателем, поэтому приведенная выше структура может использоваться для защиты любого типа значения в 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.

person Sal    schedule 20.01.2012

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

person Volker Stolz    schedule 18.01.2012