;;; ;;; A simple demo for macro QRUN-ON-UI-THREAD* / QRUN* ;;; ;;; Calculate primes in threads and update QTreeWidget directly from threads. ;;; ;;; ----------------------------------------------------------------------------- ;;; ;;; Note: ;;; ;;; Alternatively you could just load the file "~/eql5/lib/thread-safe.fas", and ;;; forget about QRUN* (that is, every EQL function call would be automatically ;;; thread-safe). ;;; ;;; The advantage of using QRUN* manually is that you can group together several ;;; function calls, which will add only one thread switch instead of several ones ;;; (resulting in better performance). ;;; (defvar *tree-widget* (qnew "QTreeWidget" "alternatingRowColors" t "size" '(500 300))) (defvar *start-time*) (defun new-item (column text) ;; QRUN* body runs in GUI/main thread (safe because queued and blocking) ;; - queued: asynchroneous call in thread of main event loop ;; - blocking: for return value(s) (qrun* (let ((item (qnew "QTreeWidgetItem"))) (x:do-with item (|setTextAlignment| 0 |Qt.AlignRight|) (|setText| 0 (format nil "~:D" (- (get-internal-real-time) *start-time*))) (|setText| column text)) (x:do-with *tree-widget* (|addTopLevelItem| item) (|resizeColumnToContents| 0) (|resizeColumnToContents| column) (|scrollToBottom|))))) (defun primep (x) ;; slow/dumb (or (= 2 x) (and (integerp x) (> x 2) (not (zerop (mod x 2))) (loop :for i :from 3 :to (isqrt x) :by 2 :never (zerop (mod x i)))))) (defun primes (column start number) (qrun* (|clear| *tree-widget*)) (setf *start-time* (get-internal-real-time)) (do ((i start (1+ i)) (found 0)) ((= found number) (new-item column "Done")) (when (primep i) (incf found) (new-item column (princ-to-string i))))) (defun list-all-threads () (format t "~%Threads:~%~%~{ ~S~%~}~%" (reverse (mp:all-processes)))) (defun run (&optional (number-threads 2)) (x:do-with *tree-widget* (|setColumnCount| (1+ number-threads)) (|setHeaderLabels| (cons "Time" (loop :for i :from 1 :to number-threads :collect (format nil "Thread ~D" i))))) (let ((column 0)) ; needed because of thread race condition (dotimes (n number-threads) (mp:process-run-function (format nil "Primes ~D" (1+ n)) (lambda () (primes (incf column) #.(expt 10 12) 15))))) (x:do-with *tree-widget* |show| |raise|) (list-all-threads)) (run)