在C中实现MVar?

Sal*_*Sal 11 c synchronization haskell ffi

MVar在C中是否有任何已知的Haskell实现?有一个关于如何在C++中实现它的例子.但是,我想在C中实现它 - 让我们现在只说MVar CIntC中的等价物.编写同步原语可能很棘手.所以,如果有人已经做过,我希望避免重复劳动.我完全不理解上面的C++示例,可以自信地将其转换为C语言 - 它从我的C++中很好地隐藏了算法细节 - 缺乏经验的头脑:)

我想在C中编写MVar的原因是因为它使我很容易使用FFI绑定到外部C库来获取数据流,并使用Haskell线程来获取数据(来自Storable向量以避免编组数据 - MVar CInt在这里存储了多少可存储的向量.我需要确保在Haskell线程读取数据时阻塞写入Storable位置的C线程.这就是C侧的MVar同步有帮助的地方.从Haskell调用不安全甚至安全的C函数(在我的测试中安全性约为15ns,在测试中安全性约为150ns)比从C调用Haskell(~5us)要快得多.如果回调很快,我会将C函数回调到Haskell,并在Haskell MVar上阻塞.

更新:

伪代码中的算法也可以.考虑到newEmptyMVar,takeMVar和putMVar的算法,在C中实现它应该很容易.

Sal*_*Sal 4

MVar 可以使用如下结构在 C 中实现:

typedef struct{
  pthread_cond_t put_cond;
  pthread_cond_t take_cond;
  pthread_mutex_t lock;
  void* value;
} mvar;
Run Code Online (Sandbox Code Playgroud)

put_cond由将值放入 M​​Var 的线程用来向其他正在等待从 MVar 获取值的线程发出信号。take_cond是 take 的类似对应物。至于调度,是默认调度。

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;
}
Run Code Online (Sandbox Code Playgroud)

MVar- 使用上面的函数:

mvar* newEmptyMVar(){
 return newMVar(NULL);
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

完整的代码位于github上,其中示例演示了如何使用 MVar。

如果只有一个线程访问它(并且争用很严重),MVar 会相当快。但是,在激烈的争用和多个线程(甚至两个)的情况下,它的扩展性非常差。鉴于 pthread 的工作方式,这并不奇怪。我发现 Haskell 中的 MVar 对于多线程非常有用。考虑到 GHC 中轻量级线程和并发原语的实现有多好,这并不奇怪。