【问题标题】:Simulating coroutines with POSIX threads (in guile scheme)使用 POSIX 线程模拟协程(在 guile 方案中)
【发布时间】:2019-05-31 15:41:47
【问题描述】:

我昨天摸索到,带有条件变量的互斥锁类似于协程的想法,如果调用者线程等待被调用者线程发出其执行的信号。

想法是有2个线程以协作方式,互斥锁代表“执行锁”。

试图验证我最喜欢的方案的想法。在我将想法扩展到 2 个线程之前,该实现运行良好。当迭代次数达到 8000 次时,线程会稍微出现故障。

我真的不明白为什么有时线程的顺序错误。如果他们这样做了,程序根本就不应该工作,因为在所有相互等待的情况下,如果程序的算法错误,就会发生死锁。真的很想了解一下。

到目前为止的代码如下:

(use-modules (ice-9 threads))

(define mtx1 (make-mutex))
(define mtx2 (make-mutex))
(define cv1 (make-condition-variable)) ;; cv1: B -> A
(define cv2 (make-condition-variable)) ;; cv2: B -> C
(define cv3 (make-condition-variable)) ;; cv3: A -> B
(define cv4 (make-condition-variable)) ;; cv4: C -> B
(define v 0)

(lock-mutex mtx1) ;; block t1
(lock-mutex mtx2) ;; block t2

(define (B->A)
  (signal-condition-variable cv1) ;; signal B -> A is going to happen
  (wait-condition-variable cv3 mtx1)) ;; release mtx1 and wait for A -> B

(define (B->C)
  (signal-condition-variable cv2) ;; signal B -> C is going to happen
  (wait-condition-variable cv4 mtx2)) ;; release mtx2 and wait for C -> B

(define (A->B)
  (signal-condition-variable cv3) ;; signal A -> B is going to happen
  (wait-condition-variable cv1 mtx1)) ;; release mtx1 and wait for B -> A

(define (C->B)
  (signal-condition-variable cv4) ;; signal C -> B is going to happen
  (wait-condition-variable cv2 mtx2)) ;; release mtx2 and wait for B -> C

(call-with-new-thread
 (lambda ()
   (lock-mutex mtx1) ;; wait for B release mtx1
   (let A ()
     (A->B)
     (set! v (+ v 1))
     (format #t "A: v=~a~%" v)
     (A))))

(call-with-new-thread
 (lambda ()
   (lock-mutex mtx2) ;; wait for B to release mtx2
   (let C ()
     (C->B)
     (set! v (+ v 1))
     (format #t "C: v=~a~%" v)
     (C))))

(wait-condition-variable cv3 mtx1) ;; trigger first execution of A, resume by A->B
(wait-condition-variable cv4 mtx2) ;; trigger first execution of C, resume by C->B

(let B ()
  (set! v (+ v 1))
  (format #t "B: v=~a~%" v)
  (B->A)
  (B->C)
  (B))

您可以使用 shell sn-p 来测试程序,看看它是如何出错的:

for (( i=1 ; ; i+=1 )) do
  echo "=== Run $i ==="
  MD5_1=$(guile message.scm |tee "/tmp/message_$i.txt" |head -10000 |md5sum)
  if [[ $i -gt 1 && "$MD5_2" != "$MD5_1" ]]; then 
    echo "bug"
    break
  fi
  MD5_2="$MD5_1"
done

我已经实现了一个等效的 C 版本。好像按照逻辑可以正常工作!

#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mtx2 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv1;
pthread_cond_t cv2;
pthread_cond_t cv3;
pthread_cond_t cv4;
int v = 0;

void BA(void) {
  pthread_cond_signal(&cv1);
  pthread_cond_wait(&cv3, &mtx1);
}

void AB(void) {
  pthread_cond_signal(&cv3);
  pthread_cond_wait(&cv1, &mtx1);
}

void BC(void) {
  pthread_cond_signal(&cv2);
  pthread_cond_wait(&cv4, &mtx2);
}

void CB(void) {
  pthread_cond_signal(&cv4);
  pthread_cond_wait(&cv2, &mtx2);
}

void *A(void *args) {
  pthread_mutex_lock(&mtx1);
  for (;;) {
    AB();
    v += 1;
    printf("A: v=%d\n", v);
  }
}

void *C(void *args) {
  pthread_mutex_lock(&mtx2);
  for (;;) {
    CB();
    v += 1;
    printf("C: v=%d\n", v);
  }
}

int main() {
  pthread_t t1, t2;

  pthread_mutex_lock(&mtx1);
  pthread_mutex_lock(&mtx2);

  pthread_create(&t1, NULL, A, NULL);
  pthread_create(&t2, NULL, C, NULL);

  pthread_cond_wait(&cv3, &mtx1);
  pthread_cond_wait(&cv4, &mtx2);

  for (;;) {
    v += 1;
    printf("B: v=%d\n", v);
    BA();
    BC();
  }

  return 0;
}

【问题讨论】:

    标签: multithreading pthreads scheme coroutine


    【解决方案1】:

    在另一个 C 实现的帮助下,这意味着 guile 方案无法正常运行。

    C 实现按预期工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-03-15
      • 1970-01-01
      • 1970-01-01
      • 2020-09-10
      • 2013-10-23
      • 2011-09-16
      • 1970-01-01
      相关资源
      最近更新 更多