Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/apache/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
“安全地”;借出;内存块到C中的另一个线程,假设没有;“并发访问”; 问题_C_Multithreading_Thread Safety_Nim Lang - Fatal编程技术网

“安全地”;借出;内存块到C中的另一个线程,假设没有;“并发访问”; 问题

“安全地”;借出;内存块到C中的另一个线程,假设没有;“并发访问”; 问题,c,multithreading,thread-safety,nim-lang,C,Multithreading,Thread Safety,Nim Lang,我想在一个线程中分配内存,并安全地将指针“借给”另一个线程,以便它可以读取该内存 我使用的是一种高级语言,可以翻译成。高级语言有线程(未指定的线程API,因为它是跨平台的——见下文),并支持标准的C多线程原语,如原子比较交换,但它并没有真正的文档化(没有使用示例)。 这种高级语言的限制条件是: 每个线程执行一个事件处理无限循环 每个线程都有自己的本地堆,由一些自定义分配器管理 每个线程都有一个“输入”消息队列,该队列可以包含来自任意数量的其他线程的消息 消息传递队列包括: 对于固定类型的消息

我想在一个线程中分配内存,并安全地将指针“借给”另一个线程,以便它可以读取该内存

我使用的是一种高级语言,可以翻译成。高级语言有线程(未指定的线程API,因为它是跨平台的——见下文),并支持标准的C多线程原语,如原子比较交换,但它并没有真正的文档化(没有使用示例)。 这种高级语言的限制条件是:

  • 每个线程执行一个事件处理无限循环
  • 每个线程都有自己的本地堆,由一些自定义分配器管理
  • 每个线程都有一个“输入”消息队列,该队列可以包含来自任意数量的其他线程的消息
  • 消息传递队列包括:
  • 对于固定类型的消息
  • 使用复制
现在,这对于大型(不需要副本)或可变大小(我认为数组大小是类型的一部分)消息是不切实际的。我想发出这样的信息,以下是我想如何实现这一目标的概要:

  • 消息(请求或回复)可以内联存储“有效负载”(复制的、固定的总值大小限制),也可以在发送方堆中存储指向数据的指针
  • 消息内容(发送方堆中的数据)归发送线程所有(分配和释放)
  • 接收线程在处理完消息内容后向发送线程发送一个ack
  • “发送”线程在收到(ack)之前,不得在发送消息内容后修改消息内容
  • 在写操作完成之前,决不能对正在写入的内存进行并发读访问这应该由消息队列工作流程来保证。
我需要知道如何确保这在没有数据竞争的情况下工作。我的理解是我需要使用内存围栏,但我不完全确定哪一个(原子释放,…)以及在循环中的什么位置(或者我是否需要)


可移植性考虑 因为我的高级语言需要跨平台,所以我需要解决以下问题:

  • Linux、MacOS以及可选的Android和iOS
    • 使用pthreads原语锁定消息队列:
      pthread\u mutex\u init
      pthread\u mutex\u lock
      +
      pthread\u mutex\u unlock
  • 窗户
    • 使用Critical Section对象锁定消息队列:
      初始化CriticalSection
      ,和
      进入CriticalSection
      +
      离开CriticalSection
如果有帮助,我假设以下架构:

  • 针对Windows/Linux/MacOS(?)的Intel/AMD PC体系结构
  • iOS和Android的未知(ARM?)
并使用以下编译器(您可以假设所有编译器的“最新”版本):

  • Windows上的MSVC
  • Linux上的叮当声
  • MacOS/iOS上的Xcode
  • Android上的Android代码
到目前为止,我只在Windows上构建了应用程序,但当应用程序完成后,我想用最少的工作量将其移植到其他平台。因此,我试图从一开始就确保跨平台兼容性


尝试解决方案 以下是我假设的工作流程:

  • 读取队列中的所有消息,直到其为空(如果完全为空,则仅阻塞)
  • 这里叫什么“记忆栅栏”
  • 读取消息内容(消息中指针的目标),并处理消息。
    • 如果消息是“请求”,则可以对其进行处理,并将新消息缓冲为“答复”
    • 如果消息是“回复”,则可以释放原始“请求”的消息内容(隐式请求“ack”)
    • 如果消息是“回复”,并且它本身包含指向“回复内容”的指针(而不是“内联回复”),那么也必须发送“回复确认”
  • 这里叫什么“记忆栅栏”
  • 将所有缓冲消息发送到相应的消息队列中
  • 真实代码太大,无法发布。下面是使用互斥锁(如消息队列)简化的伪代码(仅足以显示如何访问共享内存):

    这个解决方案有效吗?重新表述问题,线程B是否打印“42”? 始终,在所有考虑过的平台和操作系统(pthreads和windowscs)上? 还是需要添加其他线程原语,如内存围栏?


    研究 我花了好几个小时研究了许多相关的SO问题,读了一些文章,但我仍然不能完全确定。基于@Art comment,我可能不需要做任何事情。我相信这是基于POSIX标准4.12内存同步中的以下声明:

    […]使用同步线程执行以及与其他线程同步内存的函数。以下函数与其他线程同步内存

    我的问题是,这句话没有清楚地说明它们是指“所有访问的内存”,还是“仅在锁定和解锁之间访问的内存”。我读过有人对这两种情况都有争议,甚至有人暗示这是故意写得不准确的,以便给编译器实现者在实现中提供更多的余地

    此外,这适用于pthreads,但我需要知道它如何适用于Windows线程

    我将根据标准文档或其他高度可靠的来源中的引用/链接,选择证明我不需要围栏或显示我需要的围栏的任何答案,在上述平台配置下,至少在Windows/Linux/MacOS情况下。如果Windows线程的行为与本例中的pthreads类似,那么我也需要一个链接/引用

    以下是我读到的一些(最好的)相关问题/链接,但冲突信息的存在使我怀疑我的理解
    static pointer p = null
    static mutex m = ...
    static thread_A_buffer = malloc(...)
    
    Thread-A:
      do:
        // Send pointer to data
        int index = findFreeIndex(thread_A_buffer)
        // Assume different value (not 42) every time
        thread_A_buffer[index] = 42
        // Call some "memory fence" here (after writing, before sending)?
        lock(m)
        p = &(thread_A_buffer[index])
        signal()
        unlock(m)
        // wait for processing
        // in reality, would wait for a second signal...
        pointer p_a = null
        do:
          // sleep
          lock(m)
          p_a = p
          unlock(m)
        while (p_a != null)
        // Free data
        thread_A_buffer[index] = 0
        freeIndex(thread_A_buffer, index)
      while true
    
    Thread-B:
      while true:
        // wait for data
        pointer p_b = null
        while (p_b == null)
          lock(m)
          wait()
          p_b = p
          unlock(m)
        // Call some "memory fence" here (after receiving, before reading)?
        // process data
        print *p_b
        // say we are done
        lock(m)
        p = null
        // in reality, would send a second signal...
        unlock(m)
    
    int index = findFreeIndex(thread_A_buffer)
    
    lock(m)
    p = &(thread_A_buffer[index])
    signal()
    unlock(m)
    
    thread_A_buffer[index] = 42;      // happens before 
    
    lock(m);  // acquire
    
    p = &thread_A_buffer[index];
    unlock(m);
    
    thread_A_buffer[index] = 42;  << This happens before and ...
    p = &thread_A_buffer[index];  << carries a dependency into p
    unlock(m);
    
    pointer p_a = null
    do:
      // sleep
      lock(m)
      p_a = p
      unlock(m)
    while (p_a != null)