在C中有Haskell MVar的任何已知实现吗?关于如何在C++中实现它,有一个example。但是,我想在C中实现它-现在让我们只说等效于C的MVar CInt。编写同步原语可能很棘手。因此,如果有人已经做过,我将避免重复工作。我对上述C++示例的理解不够充分,无法自信地将其转换为C-对我的C++经验不足的人来说,它很好地隐藏了算法细节:)

我之所以考虑使用C语言编写MVar,是因为它使我很容易使用FFI绑定(bind)到外部C库来获取数据流,并使用Haskell线程来获取数据(从可存储 vector 中避免整理数据-此处的MVar CInt存储已填充了多少可存储 vector 。我需要确保在Haskell线程读取数据时,阻止写到Storable位置的C线程。这就是C端MVar同步有帮助的地方。从Haskell调用不安全甚至安全的C函数(在我的测试中,不安全约15ns,在测试中安全约150ns)要比从C回调到Haskell(约5us)要快得多。如果回调速度很快,那么我本来可以让C函数回调到Haskell,然后在Haskell MVar上阻塞。

更新:

伪代码中的算法也可以。给定newEmptyMVar,takeMVar和putMVar的算法,用C来实现它应该很容易。

最佳答案

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外部写入该指针-因此,程序有责任确保它不会发生(通过避免在MVar外部松散value指针-始终通过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上,其中example显示了如何使用MVar。

如果只有一个线程访问它,则MVar会非常快(而且争用很重)。但是,在激烈的争用和多个线程(甚至两个)的情况下,它的伸缩性很差。由于pthread的工作方式,这不足为奇。我发现Haskell中的MVar在多线程方面非常出色。鉴于在GHC中实现轻量级线程和并发原语的程度很好,这不足为奇。

10-07 14:57