用 C 语言实现 MVar?

发布于 2024-12-27 17:04:18 字数 730 浏览 1 评论 0 原文

是否有任何已知的 Haskell MVar 用 C 实现?有一个关于如何在 C++ 中实现它的示例。但是,我想用 C 实现它 - 现在我们只说 C 中的 MVar CInt 等效项。编写同步原语可能很棘手。因此,如果有人已经这样做了,我希望避免重复工作。我对上面的 C++ 示例理解不够充分,无法自信地将其翻译为 C - 它很好地隐藏了我没有 C++ 经验的头脑中的算法细节:)

我考虑用 C 编写 MVar 的原因是因为它使它变得非常容易对我来说,使用 FFI 绑定到外部 C 库来获取数据流,并使用 Haskell 线程来获取数据(从可存储向量中获取数据,以避免封送数据 - MVar CInt 这里存储了多少数据)可存储向量已被填充)。我需要确保在 Haskell 线程读取数据时,写入可存储位置的 C 线程被阻止。这就是 C 端 MVar 同步的帮助之处。从 Haskell 调用不安全甚至安全的 C 函数(在我的测试中,不安全约 15 纳秒,安全约 150 纳秒)比从 C 回调到 Haskell(约 5 秒)要快得多。如果回调速度很快,我会让 C 函数回调回 Haskell,并阻止 Haskell MVar。

更新:

伪代码算法也可以。考虑到 newEmptyMVar、takeMVar 和 putMVar 的算法,用 C 语言实现它应该很容易。

Is there any known implementation of Haskell MVar in C? There is an example on how to implement it in C++. But, I will like to implement it in C - let us say only MVar CInt equivalent in C for now. Writing synchronization primitives can be tricky. So, I will like to avoid duplication of effort if someone has already done it. I didn't understand the C++ example above well enough to confidently translate it into C - it hides the algorithmic details very well from my C++-inexperienced mind :)

The reason I am thinking about writing MVar in C is because it makes it really easy for me to use the FFI binding to an external C library to get the stream of data, and use Haskell threads to grab the data (from Storable vectors to avoid marshaling the data - MVar CInt here stores how much of the Storable vectors have been filled). I need to make sure that C threads writing to Storable locations are blocked while a Haskell thread is reading the data. That is where MVar synchronization on C side helps. It is also much faster to call unsafe or even safe C function from Haskell (~15ns for unsafe, ~150ns for safe in my test), than callback into Haskell from C (~5us). If callbacks were fast, I would have had C function call back into Haskell instead, and block on Haskell MVar.

Update:

Algorithm in pseudo-code will do as well. It should be quite easy to implement it in C, given the algorithm for newEmptyMVar, takeMVar and putMVar.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

所谓喜欢 2025-01-03 17:04:18

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

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

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;
}

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 会相当快。但是,在激烈的争用和多个线程(甚至两个)的情况下,它的扩展性非常差。鉴于 pthread 的工作方式,这并不奇怪。我发现 Haskell 中的 MVar 对于多线程非常有用。考虑到 GHC 中轻量级线程和并发原语的实现有多好,这并不奇怪。

MVar can be implemented in C using a struct like below:

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

put_cond is used by threads that put values in MVar to signal other threads that are waiting to take value from MVar. take_cond is the analogous counterpart for take. As for scheduling, it is default scheduling.

value is a void pointer - so, the above structure can be used to protect any type of value in an MVar - of course, C will let you write that pointer outside MVar - so, it is the program's responsibility to ensure this doesn't happen (by avoiding squirreling away value pointer outside MVar - always access it through MVar functions).

Initializing 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;
}

Empty MVar - uses above function:

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
}

Complete code is on github, with example which shows how to use MVar.

MVar is quite fast if there is only one thread accessing it (and heavy contention). But, under heavy contention, and multiple threads (even two), it scales very poorly. This is not a surprise because of the way pthreads work. I have found MVar in Haskell to be very good with multiple threads. This is not a surprise given how well lightweight threads and concurrency primitives are implemented in GHC.

一袭水袖舞倾城 2025-01-03 17:04:18

示例中的代码不太特定于 C++。基本位正是 pthread 片段。

The code in the example is not very C++-specific. The essential bits are exactly the pthread-fragments.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文