1

ユーザーがクリックすると「惑星」を作成し、チェックボックスがクリックされると惑星が互いに周回するのを開始/停止する割り当てのスキームプログラムを書いています。これをスレッドで実装することになっています。ただし、チェックボックスをクリックしてもスレッドサスペンドは機能しないようですが、再開は機能します。

ご協力いただきありがとうございます。コードは次のとおりです。

#lang racket

(require racket/gui)
(require racket/block)

;; Small 2d vector library for the Newtonian physics
(define (x v) (vector-ref v 0))
(define (y v) (vector-ref v 1))
(define (x! v value) (vector-set! v 0 value))
(define (y! v value) (vector-set! v 1 value))
(define (v* v value) (vector-map (lambda (x) (* x value)) v))
(define (v+ v w) (vector-map + v w))
(define (v- v w) (vector-map - v w))
(define (v-zero! v) (vector-map! (lambda (x) 0) v))
(define (v-dot v w) (let ((vw (vector-map * v w))) (+ (x vw) (y vw))))
(define (v-mag v) (sqrt (v-dot v v)))
(define sem (make-semaphore))


;; Planet object
(define planet%
  (class object%
(public m p v calculate-force move draw)
(init-field (mass 1)
            (position (vector 0 0 ))
            (velocity (vector 0 0 ))
            (force (vector 0 0 )))
(define (m) mass)
(define (p) position)
(define (v) velocity)
;; Use Newton's law of gravitation.
;; I assume the gravitational constant is one
(define (calculate-force planet-list)
  (v-zero! force)
  (for-each (lambda (other-planet)
              (when (not (equal? this other-planet))
                (let* ((direction (v- (send other-planet p) position))
                       (dist (max 1 (v-mag direction)))
                       (other-mass (send other-planet m))
                       (new-force (v* direction (/ (* mass other-mass) (* dist dist))))
                      )
                  (vector-map! + force new-force))))
            planet-list)
  )
;; Simple Euler integration of acceleration and velocity
(define (move) 
  (let ((acc (v* force (/ 1.0 mass))))
    (vector-map! + velocity acc)
    (vector-map! + position velocity)))
;; Draw a circle 
(define (draw dc) 
  (send dc set-brush brush)
  (send dc set-pen pen)
  (send dc draw-ellipse (x position) (y position) radius radius ))
;; Initialize to random velocity, mass, and color
(x! velocity (random))
(y! velocity (random))
(set! mass (+ 1 (* 10 (random))))
(define radius (* 5 (sqrt mass)))
(define color 
  (let* ((r (random))
         (b (real->floating-point-bytes r 4)))
    (make-object color% (bytes-ref b 0) (bytes-ref b 1) (bytes-ref b 2) )))
(define brush (make-object brush% color))
(define pen (make-object pen% color))
;; Don't forget the super-new!
(super-new)
))
;; Abstract the list-handling for a list of planets
(define planet-list%
  (class object%
(public add-planet calculate-force move draw)
(init-field (planets '()))
(define (add-planet planet)
  (set! planets (cons planet planets)))
(define (calculate-force)
  (for-each (lambda (planet)
              (send planet calculate-force planets))
            planets))
(define (move)
  (for-each (lambda (planet)
              (send planet move))
            planets))
(define (draw dc)
  (for-each (lambda (planet)
              (send planet draw dc))
            planets))
(super-new)
)
  )
(define planet-list (new planet-list%))

;; The GUI
(define frame (new frame% 
               (label "Planets")
               (min-width 120)
               (min-height 80)
               ))
(send frame create-status-line)
(send frame show #t)

(define h-panel
  (new horizontal-panel%
   (parent frame)
   (stretchable-height #f)
   (style '(border))
   (border 2)))

(define run-checkbox
  (new check-box%
   (parent h-panel)
   (label "Run animation")
   (callback
    (lambda (button event)
      (cond [(send run-checkbox get-value)(thread-resume (thread-a))]
            [(not (send run-checkbox get-value)) (thread-suspend (thread-a))]
   )))
    ))

(define my-canvas%
  (class canvas%
(override on-paint on-event)

(define (on-paint)
  (let ((dc (send this get-dc))
        (w (send this get-width))
        (h (send this get-height)))
    (send dc clear)
    (send planet-list draw dc)
    ))
(define (on-event event)
  (when (send event button-down?)
    (let ((x (send event get-x))
          (y (send event get-y)))
      (send frame set-status-text (format "Mouse at ~a ~a" x y))
      (send planet-list add-planet (new planet% (position (vector x y))))

      (send this refresh)))
  )
(super-new)
(send (send this get-dc) set-background (make-object color% 8 8 64))
))

(define canvas
  (new my-canvas%
   (parent frame)
   (style '(border))
   (min-width 640)
   (min-height 480)))

;; planet animator
(define thread-a (lambda ()
(let loop ()
  (sleep/yield .1)
(send planet-list calculate-force)
(send planet-list move)
(send canvas refresh)
  (loop))))

; this creates the thread-a and starts the program

(thread-suspend (thread thread-a))
4

1 に答える 1

3

これが同じように機能するようになったのは、実際には奇跡的です。

問題は、それthread-aがスレッドではないということです。スレッドを生成する関数ではありません。これは、惑星を動かしたり、キャンバスを更新したりして、永遠に実行される機能です。

したがって、(thread-suspend (thread-a))たとえば、チェックボックスのコールバックが実行された場合、thread-suspend実際には発生しません。の呼び出しthread-aは実行を開始するだけで、戻ることはありません。

GUIがロックアップしない理由(イベントコールバックが返されない場合は通常そうなります)は、thread-a定期的にを呼び出すsleep/yieldためです。これにより、GUIイベントループがより多くのイベントを処理できるようになります。(だから私はコードが奇跡的だと言ったのです。)

thread-a修正は、スレッド自体として定義することです。

(define thread-a
  (thread
    (lambda ()
      (let loop () ....))))
(thread-suspend thread-a)

他の参照を(thread-a)からだけに変更しthread-aます。

于 2013-01-26T22:34:28.173 に答える