Головоломки с многопоточностью
Я пытаюсь придумать несколько головоломок для программирования, ориентированных на многопоточность. До сих пор большинство проблем, с которыми мне приходилось сталкиваться, были довольно специфичны для конкретной области. У кого-нибудь есть достойные головоломки программирования для разработчиков, пытающихся изучить основные концепции многопоточных приложений?
12 ответов
По этой ссылке рассмотрен ряд тем.
Многопоточное программирование с ThreadMentor: учебное пособие
Редактировать:
Вот несколько прямых ссылок на проблемы, перечисленные по этой ссылке, а также их первоначальные описания.
ThreadMentor: проблема обедающего философа
ThreadMentor: Обеденная философская проблема: Левосторонняя версия
Задача столовых философов придумана Э. У. Дейкстра. Представьте себе, что пять философов проводят свою жизнь, просто размышляя и ориентируясь на восток. В середине столовой находится круглый стол с пятью стульями. На столе есть большая тарелка спагетти. Однако доступно только пять палочек для еды, как показано на следующем рисунке. Каждый философ думает. Когда он проголодался, он садится и берет две палочки, которые находятся ближе всего к нему. Если философ может взять обе палочки, он некоторое время ест. После того, как философ заканчивает есть, он кладет палочки для еды и начинает думать.
ThreadMentor: проблема курильщика сигарет
Эта проблема возникла у С.С. Патила в 1971 году. Предположим, сигарете нужны три ингредиента: табак, бумага и спичка. Есть три заядлых курильщика. У каждого из них есть только один ингредиент с бесконечным запасом. Есть агент, который имеет бесконечный запас всех трех ингредиентов. Чтобы сделать сигарету, у курильщика есть табак (соответственно, бумага и спичка), должны быть два других ингредиента - бумага и спичка (соответственно, табак и спичка, а также табак и бумага). Агент и курильщики делят стол. Агент случайным образом генерирует два ингредиента и уведомляет курильщика, которому нужны эти два ингредиента. Как только ингредиенты взяты со стола, агент поставляет еще два. С другой стороны, каждый курильщик ожидает уведомления агента. Получив уведомление, курильщик берет ингредиенты, закуривает, некоторое время курит и возвращается к столу в ожидании следующих ингредиентов.
ThreadMentor: проблема производителя / потребителя (или ограниченного буфера)
Предположим, у нас есть циклический буфер с двумя указателями на вход и выход для указания следующей доступной позиции для размещения данных и позиции, которая содержит следующие данные для извлечения. Смотрите схему ниже. Есть две группы потоков, производители и потребители. Каждый производитель помещает элементы данных в позицию in и продвигает указатель внутрь, а каждый потребитель извлекает элемент данных в позиции out и продвигает указатель наружу.
ThreadMentor: проблема американских горок
Предположим, что есть n пассажиров и одна машина американских горок. Пассажиры многократно ждут, чтобы покататься на автомобиле, который может вместить максимум пассажиров C, где C
У этого есть дополнительные ограничения:
- Автомобиль всегда едет точно с пассажирами;
- Пассажиры не спрыгнут с машины, пока она едет;
- Пассажиры не будут прыгать на машине во время движения;
- Пассажиры не будут просить еще одну поездку, прежде чем они смогут выйти из машины.
Описание этого опирается на изображения. Вот модифицированная цитата с удаленными ссылками на изображения.
Рассмотрим узкий мост, который позволяет одновременно пересекать только три автомобиля в одном направлении. Если на мосту три автомобиля, то любой входящий автомобиль должен дождаться, пока мост не очистится.
Когда транспортное средство покидает мост, у нас есть два случая для рассмотрения. Случай 1, есть другие транспортные средства на мосту; и в случае 2 выходящий автомобиль является последним на мосту. В первом случае одно новое транспортное средство в том же направлении должно быть разрешено для движения.
Случай 2 является более сложным и имеет два случая. В этом случае выходящее транспортное средство является последним транспортным средством на мосту. Если есть автомобили, ожидающие в противоположном направлении, один из них должен быть допущен. Или, если нет транспортного средства, ожидающего в противоположном направлении, то оставьте ожидающее транспортное средство в том же направлении, чтобы продолжить.
У вас большая древовидная структура в памяти. Многие темы должны искать в структуре. Иногда потоку необходимо вставить или удалить что-либо из структуры. Как вы контролируете доступ к структуре, чтобы программа работала правильно (никакие два потока не будут нагнетать друг друга при изменении структуры) и эффективно (никакие потоки не блокируются, если они не должны быть)?
Возможно, вы можете использовать простую задачу тестирования и установки общего флага или доступа к какому-либо списковому ресурсу каким-то последовательно согласованным способом?
В зависимости от того, что вы делаете со своей многопоточностью, это имеет значение.
Вы в банке. Клиенты приезжают со средней скоростью 1 раз в 2 минуты. Каждый клиент обслуживается в среднем за 2 минуты.
Какое решение лучше обслуживать клиентов? Одна общая линия или одна строка для каждого кассира?
Достаточно ли вашего выбора, чтобы гарантировать некоторую привязку к длине линии?
Ответы: из-за свойства markov прибытия клиента и фактического времени обслуживания на человека линия никогда не будет знать границы. Кроме того, это хорошая идея, чтобы заставить их ждать в одну общую линию, хотя этого недостаточно, чтобы преодолеть безграничную линию.
Вот параллельный решатель N-головоломки, реализованный в PARLANSE. Язык имеет LISP-подобный синтаксис, но действительно ближе к C (скаляры, структуры, указатели, вызовы функций), но в отличие от C имеет локальные области действия. Секрет заключается в параллельном операторе разветвления (|| ...), который выполняет все свои операнды параллельно, а также в способности PARLANSE использовать исключения для остановки родительских гранул.
Этот решатель обеспечивает линейное ускорение на всех машинах с 4 и 8 путями, на которых я его пробовал.
(define Version `N-puzzle Solver V1.1~l
Copyright (C) 1998-2009 Semantic Designs; All Rights Reserved~l')
(define SolveParticularPuzzle ~t)
(define ManhattanHeuristic ~t) ; Manhattan is really fast
(define PrintTrace ~f)
(include `parmodule.par')
(define ScrambleCount 10000)
(define PuzzleSize `Length of side of N-puzzle' +4) ; at least 3!
(define PuzzleSizeMinus1 +3)
(define PuzzleArea `Area of puzzle (= (-- N))' +16) ; (= (* PuzzleSize PuzzleSize))
(define PuzzleAreaMinus1 +15)
(define BlankTile `Code for a blank tile' 0)
(define puzzlepieceT `Codes for nonblank tiles'
(sort natural (range 1 PuzzleArea)))
(define BoardPositionT integer) ; normally positive, but sometime we reach off the edge
(define ConfigurationT (array puzzlepieceT 0 PuzzleAreaMinus1))
(define HardPuzzle1 `Solution found of length 29:
2 1 5 6 2 3 7 11 10 6 2 3 7 11 10 14 13 9 8
12 13 9 5 1 2 6 5 1 0'
(lambda (function ConfigurationT void)
(make ConfigurationT 01 11 02 00
04 06 09 05
13 12 07 03
08 14 10 15)
)lambda
)define
(define HardPuzzle2 `Solution found of length 31:
0 4 5 6 10 9 5 1 2 3 7 6 10 9 5 1
2 3 7 6 5 1 2 6 1 0 14 13 9 5 4 0'
(lambda (function ConfigurationT void)
(make ConfigurationT 13 00 02 09
04 05 06 01
08 07 03 11
12 14 10 15)
)lambda
)define
(define HardPuzzle3 `Solution found of length 56:
1 2 6 7 3 2 6 10 14 15 11 10 9 5
4 8 12 13 9 10 6 5 1 0 4 8 12 13
14 10 6 7 11 10 9 13 14 15 11 10
6 5 4 8 9 10 6 5 1 0 4 8 9 5 4 0
Total solution time in seconds: 18-24 (on 8 processor machine)'
(lambda (function ConfigurationT void)
(make ConfigurationT 00 09 10 08
15 12 03 02
01 11 13 14
06 04 07 05)
)lambda
)define
(define HardPuzzle4 `Solution found of length 50:
4 5 1 0 4 8 12 13 9 5 1 0 4 5 6
10 14 13 9 8 4 5 6 2 1 5 9 10 14
13 12 8 9 10 11 15 14 13 9 10 11
7 3 2 1 5 9 8 4 0
Total solution time in seconds: 125 (on 8 processor machine)'
(lambda (function ConfigurationT void)
(make ConfigurationT 00 15 06 07
12 03 08 11
04 13 02 05
01 14 09 10)
)lambda
)define
(define HardPuzzle5
`Solution found of length 68:
3 7 11 10 6 2 3 7 6 5 9 8 4 5 1 0 4 5 9 13 14 15 11
7 6 5 1 2 6 5 9 8 12 13 14 10 6 5 4 8 12 13 14 15 11
10 9 5 1 0 4 8 12 13 9 5 4 8 9 13 14 15 11 7 3 2 1 0
Total solution time in seconds: 2790 (on 8 processor machine)'
(lambda (function ConfigurationT void)
(make ConfigurationT 15 09 00 14
10 11 12 08
03 02 13 07
01 06 05 04)
)lambda
)define
(define ParticularPuzzleToSolve HardPuzzle5)
(define PrintConfiguration
(action (procedure [Puzzle (reference ConfigurationT)])
(do [position BoardPositionT] +0 PuzzleAreaMinus1 +1
(;; (ifthenelse (<= Puzzle:position 9)
(;; (PAR:PutConsoleCharacter "0")(PAR:PutConsoleNatural Puzzle:position) );;
(PAR:PutConsoleNatural Puzzle:position)
)ifthenelse
(PAR:PutConsoleSpace)
(ifthen (== (modulo (coerce natural position) (coerce natural PuzzleSize))
(coerce natural PuzzleSizeMinus1)coerce )==
(PAR:PutConsoleNewline)
)ifthen
);;
)do
)action
)define
(define Solved? `Determines if puzzle is solved.'
(lambda (function boolean
[board (reference ConfigurationT)]
)function
(value (;; `Fast check for completed':
(ifthen (~= board:0 BlankTile)
(return ~f)
)ifthen
(do [position BoardPositionT] PuzzleAreaMinus1 +1 -1
(ifthen (~= board:position (coerce natural position))
(return ~f)
)ifthen
)do
);;
~t ; all pieces are in proper places
)value
)lambda
)define
(define ScoreT `Estimate of configuration distance from solution.
Zero means configuration is a solution.'
(sort natural (range 0 1000))) ; s/b (range 0 (* PuzzleArea PuzzleArea))
(define SolvedScore `The score of a goal position.' 0)
(define UnsolvableScore `An impossibly big score.' 12345678)
(define LowerBoundOnScore
(lambda (function ScoreT [Puzzle (reference ConfigurationT)])
(let (= [OutOfPlaceTiles ScoreT] 0)
(value
(compileifthenelse ManhattanHeuristic ; ~t for Out-of-place, ~f for Manhattan
(do [Row BoardPositionT] PuzzleSizeMinus1 +0 -1
(do [Column BoardPositionT] PuzzleSizeMinus1 +0 -1
(local (;; (= [position integer] (+ (* Row PuzzleSize)
Column))=
(= [tile puzzlepieceT] Puzzle:position)
);;
(ifthen (~= tile BlankTile) ; ignore BlankTile
(+= OutOfPlaceTiles
(+ (magnitude (- Row (coerce integer (// tile (coerce natural PuzzleSize)))))
(magnitude (- Column (coerce integer (modulo tile (coerce natural PuzzleSize)))))
)+ ; add Manhattan distance of tile from tile goal
)+=
)ifthen
)local
)do ; Column
)do ; Row
(do [position BoardPositionT] PuzzleAreaMinus1
+1 ; skipping zero effectively ignores BlankTile
+1
(ifthen (~= Puzzle:position (coerce natural position))
(+= OutOfPlaceTiles)
)ifthen
)do
)compileifthenelse
OutOfPlaceTiles ; the answer
)value
)let
)lambda
)define
(recursive PathElementT
(define PathElementT `A series of moves of the blank tile.'
(structure [Move BoardPositionT]
[Next (reference PathElementT)]
)structure
)define
)recursive
(define EmptyPath (void (reference PathElementT))void )define
(define ValuedPathT `A path and the score it acheives.'
(structure [Solved boolean]
[Score ScoreT]
[Path (reference PathElementT)])
)define
(define MakeMove `Applies a move to a configuration'
(lambda (function ConfigurationT
(structure [BlankTilePosition BoardPositionT]
[NewBlankPosition BoardPositionT]
[ConfigurationBeforeMove
(reference ConfigurationT)]
)structure )function
(let (= [ResultConfiguration ConfigurationT]
(@ ConfigurationBeforeMove) )=
(value
(;;
(compileifthen PrintTrace
(;; (PAR:PutConsoleNatural BlankTilePosition)
(PAR:PutConsoleNatural NewBlankPosition)
);;
)compileifthen
(trust (== ConfigurationBeforeMove:BlankTilePosition
BlankTile))
(= ResultConfiguration:BlankTilePosition
ConfigurationBeforeMove:NewBlankPosition)
(= ResultConfiguration:NewBlankPosition BlankTile)
);;
ResultConfiguration
)value
)let
)lambda
)define
(define TopEdge? `Determines if a position is along top edge of puzzle.'
(lambda (function boolean BoardPositionT)
(< ? PuzzleSize)
)lambda
)define
(define BottomEdge? `Determines if a position is along bottom edge of puzzle.'
(lambda (function boolean BoardPositionT)
(>= ? (- PuzzleArea PuzzleSize))
)lambda
)define
(define LeftEdge? `Determines if a position is along left edge of puzzle.'
(lambda (function boolean BoardPositionT)
(== (modulo (coerce natural ?) (coerce natural PuzzleSize)) 0)==
)lambda
)define
(define RightEdge? `Determines if a position is along right edge of puzzle.'
(lambda (function boolean BoardPositionT)
(== (modulo (coerce natural ?) (coerce natural PuzzleSize))modulo
(coerce natural PuzzleSizeMinus1)coerce )==
)lambda
)define
(define Solved! (exception (lambda (function string (reference ValuedPathT))
`N-puzzle solution is:~l'
)lambda
)exception
)define
[SerialPrint semaphore]
[MaxMoves natural]
(define Npuzzle
(lambda (function ValuedPathT
[BlankTilePosition BoardPositionT]
[PreviousBlankTilePosition BoardPositionT]
[Puzzle ConfigurationT]
[MovesToHere natural]
)function
)lambda
)define
(define Npuzzle `Solves a puzzle and generates a sequence which is a solution.'
(lambda (function ValuedPathT
[BlankTilePosition BoardPositionT]
[PreviousBlankTilePosition BoardPositionT]
[Puzzle ConfigurationT]
[MovesToHere natural]
)function
(ifthenelse (value (compileifthen PrintTrace
(;; (PAR:PutConsole (. `In Npuzzle at depth '))
(PAR:PutConsoleNatural MovesToHere) (PAR:PutConsoleNewline)
(PrintConfiguration (. Puzzle))
);;
)compileifthen
(Solved? (. Puzzle)))
(make ValuedPathT ~t 0 EmptyPath)make ; the answer
(let (|| [valuedpath1 ValuedPathT]
[valuedpath2 ValuedPathT]
[valuedpath3 ValuedPathT]
[valuedpath4 ValuedPathT]
[Best ValuedPathT]
(= [EstimatedDistance natural]
(+ MovesToHere (LowerBoundOnScore (. Puzzle)))+ )=
)||
(ifthenelse (value (compileifthen PrintTrace
(;; (PAR:PutConsole (. `Inside LET EstimatedDistance= '))
(PAR:PutConsoleNatural EstimatedDistance) (PAR:PutConsoleNewline)
);;
)compileifthen
(> EstimatedDistance MaxMoves) )
(make ValuedPathT ~f EstimatedDistance EmptyPath) ; don't explore any further
(value
(;; (assert (& (<= +0 BlankTilePosition)
(< BlankTilePosition PuzzleArea) )& )assert
; (PAR:PutConsole (. `Solve subpuzzles: blank @ '))(PAR:PutConsoleNatural BlankTilePosition)(PAR:PutConsoleNewline)
(try `Solve subpuzzles':
(|| ; replace this by (;; to see pure serial execution times
`Fork Right':
(local (|| (= [NewBlankTilePosition BoardPositionT]
(++ BlankTilePosition) )=
[ExtendedPath (reference PathElementT)]
)||
(ifthenelse (value (;; ; (PAR:PutConsole (. `Fork Right~l'))
);;
(&& (~= NewBlankTilePosition
PreviousBlankTilePosition )~=
(~ (RightEdge? BlankTilePosition))~
)&& )value
(;; (= valuedpath1
(Npuzzle NewBlankTilePosition
BlankTilePosition
(MakeMove BlankTilePosition
NewBlankTilePosition
(. Puzzle) )MakeMove
(++ MovesToHere)
)Npuzzle )=
(ifthen valuedpath1:Solved
(;; (+= valuedpath1:Score) ; since we added a move
(= ExtendedPath (new PathElementT))
(= (@ ExtendedPath) (make PathElementT NewBlankTilePosition valuedpath1:Path) )=
(= valuedpath1:Path ExtendedPath)
(raise Solved! (. valuedpath1))
);;
)ifthen
);;
(= valuedpath1 (make ValuedPathT ~f UnsolvableScore EmptyPath))=
)ifthenelse
)local
`Fork Left':
(local (|| (= [NewBlankTilePosition BoardPositionT]
(-- BlankTilePosition) )=
[ExtendedPath (reference PathElementT)]
)||
(ifthenelse (value (;; ; (PAR:PutConsole (. `Fork Left~l'))
);;
(&& (~= NewBlankTilePosition
PreviousBlankTilePosition )~=
(~ (LeftEdge? BlankTilePosition))~
)&& )value
(;; (= valuedpath2
(Npuzzle NewBlankTilePosition
BlankTilePosition
(MakeMove BlankTilePosition
NewBlankTilePosition
(. Puzzle) )MakeMove
(++ MovesToHere)
)Npuzzle )=
(ifthen valuedpath2:Solved
(;; (+= valuedpath2:Score) ; since we added a move
(= ExtendedPath (new PathElementT))
(= (@ ExtendedPath) (make PathElementT NewBlankTilePosition valuedpath2:Path) )=
(= valuedpath2:Path ExtendedPath)
(raise Solved! (. valuedpath2))
);;
)ifthen
);;
(= valuedpath2 (make ValuedPathT ~f UnsolvableScore EmptyPath))=
)ifthenelse
)local
`Fork Down':
(local (|| (= [NewBlankTilePosition BoardPositionT]
(- BlankTilePosition PuzzleSize) )=
[ExtendedPath (reference PathElementT)]
)||
(ifthenelse (value (;; ; (PAR:PutConsole (. `Fork Down~l'))
);;
(&& (~= NewBlankTilePosition
PreviousBlankTilePosition )~=
(~ (TopEdge? BlankTilePosition))~
)&& )value
(;; (= valuedpath3
(Npuzzle NewBlankTilePosition
BlankTilePosition
(MakeMove BlankTilePosition
NewBlankTilePosition
(. Puzzle) )MakeMove
(++ MovesToHere)
)Npuzzle )=
(ifthen valuedpath3:Solved
(;; (+= valuedpath3:Score) ; since we added a move
(= ExtendedPath (new PathElementT))
(= (@ ExtendedPath) (make PathElementT NewBlankTilePosition valuedpath3:Path) )=
(= valuedpath3:Path ExtendedPath)
(raise Solved! (. valuedpath3))
);;
)ifthen
);;
(= valuedpath3 (make ValuedPathT ~f UnsolvableScore EmptyPath))=
)ifthenelse
)local
`Fork Up':
(local (|| (= [NewBlankTilePosition BoardPositionT]
(+ BlankTilePosition PuzzleSize) )=
[ExtendedPath (reference PathElementT)]
)||
(ifthenelse (value (;; ; (PAR:PutConsole (. `Fork Up~l'))
);;
(&& (~= NewBlankTilePosition
PreviousBlankTilePosition )~=
(~ (BottomEdge? BlankTilePosition))~
)&& )value
(;; (= valuedpath4
(Npuzzle NewBlankTilePosition
BlankTilePosition
(MakeMove BlankTilePosition
NewBlankTilePosition
(. Puzzle) )MakeMove
(++ MovesToHere)
)Npuzzle )=
(ifthen valuedpath4:Solved
(;; (+= valuedpath4:Score) ; since we added a move
(= ExtendedPath (new PathElementT))
(= (@ ExtendedPath) (make PathElementT NewBlankTilePosition valuedpath4:Path) )=
(= valuedpath4:Path ExtendedPath)
(raise Solved! (. valuedpath4))
);;
)ifthen
);;
(= valuedpath4 (make ValuedPathT ~f UnsolvableScore EmptyPath))=
)ifthenelse
)local
) ; || or ;;
`Exception handler':
(;; ; (PAR:PutConsole (. `Exception handler~l'))
(ifthenelse (== (exception) Solved!)==
(;; (= Best (@ (exceptionargument (reference ValuedPathT))))=
(acknowledge (;; );; )acknowledge
);;
(propagate) ; oops, something unexpected!
)ifthenelse
);;
`Success handler':
(;; ; (PAR:PutConsole (. `Success (no exception raised)!~l'))
`If we get here, no result is a solution,
and all results have leaf-estimated scores.'
(ifthenelse (< valuedpath1:Score valuedpath2:Score)
(= Best valuedpath1)
(= Best valuedpath2)
)ifthenelse
(ifthen (< valuedpath3:Score Best:Score)
(= Best valuedpath3) )ifthen
(ifthen (< valuedpath4:Score Best:Score)
(= Best valuedpath4) )ifthen
);;
)try
);;
Best ; the answer to return
)value
)ifthenelse
)let
)ifthenelse
)lambda
)define
[StartTimeMicroseconds natural]
(define ElapsedTimeSeconds
`Returns time in seconds rounded to nearest integer'
(lambda (function natural void)
(/ (- (+ (MicrosecondClock) 500000) StartTimeMicroseconds) 1000000)
)lambda
)define
(define main
(action (procedure void)
(local (|| [PuzzleToSolve ConfigurationT]
[BlankTilePosition BoardPositionT]
[Solution ValuedPathT]
[BlankLocation BoardPositionT]
[Neighbor BoardPositionT]
[PathScanP (reference PathElementT)]
[ElapsedTime natural]
)||
(;; (PAR:PutConsoleString Version)
(consume (addresource SerialPrint 1))
`Set PuzzleToSolve to Solved position':
(do [position BoardPositionT] +0 PuzzleAreaMinus1 +1
(= PuzzleToSolve:position (coerce puzzlepieceT position) )=
)do
(ifthenelse SolveParticularPuzzle
(;; (PAR:PutConsole (. `Hard puzzle...~l'))
(= PuzzleToSolve (ParticularPuzzleToSolve) )= );;
(;; `Scramble puzzle position'
(PAR:PutConsole (. `Random puzzle...~l'))
(= BlankLocation +0)
(do [i natural] 1 (modulo (MicrosecondClock)
ScrambleCount)modulo 1
(;; (= Neighbor BlankLocation)
(ifthenelse (== (PAR:GetRandomNat 2) 0)
(;; `Move Blank up or down'
(ifthenelse (== (PAR:GetRandomNat 2) 0)
(ifthen (~ (TopEdge? BlankLocation)) (-= Neighbor PuzzleSize))
(ifthen (~ (BottomEdge? BlankLocation)) (+= Neighbor PuzzleSize))
)ifthenelse
);;
(;; `Move Blank left or right'
(ifthenelse (== (PAR:GetRandomNat 2) 0)
(ifthen (~ (LeftEdge? BlankLocation)) (-= Neighbor))
(ifthen (~ (RightEdge? BlankLocation)) (+= Neighbor))
)ifthenelse
);;
)ifthenelse
; (PAR:PutConsoleNatural BlankLocation)(PAR:PutConsoleNatural Neighbor)(PAR:PutConsoleSpace)
(ifthen (~= BlankLocation Neighbor)
(= PuzzleToSolve
(MakeMove BlankLocation Neighbor (. PuzzleToSolve). )MakeMove )=
)ifthen
(= BlankLocation Neighbor)=
);;
)do
);;
)ifthenelse
(;; `Initialize solver'
(= Solution:Solved ~f)
(= Solution:Score 0)
(do FindBlankTile
[position BoardPositionT] +0 PuzzleAreaMinus1 +1
(ifthen (== PuzzleToSolve:position BlankTile)
(;; (= BlankTilePosition position)
(exitblock FindBlankTile)
);; )ifthen )do
);;
(PAR:PutConsole (. `~lInitial Configuration:~l'))
(PrintConfiguration (. PuzzleToSolve))
(PAR:PutConsole (. `Estimate of solution length: '))
(PAR:PutConsoleNatural (LowerBoundOnScore (. PuzzleToSolve)))
(PAR:PutConsoleNewline)
(= StartTimeMicroseconds (MicrosecondClock))
(while (~ Solution:Solved)
(;; (critical SerialPrint 1
(;; (PAR:PutConsole (. `*** Iteration to depth '))
(PAR:PutConsoleNatural Solution:Score)
(PAR:PutConsole (. ` ')) (PAR:PutConsoleNatural (ElapsedTimeSeconds)) (PAR:PutConsole (. ` Seconds'))
(PAR:PutConsoleNewline)
);;
)critical
(= MaxMoves Solution:Score)
(= Solution (Npuzzle BlankTilePosition BlankTilePosition PuzzleToSolve 0) )=
);;
)while
(= ElapsedTime (ElapsedTimeSeconds))
(critical SerialPrint 1
(;; (PAR:PutConsole (. `Solution found of length '))
(PAR:PutConsoleNatural Solution:Score) (PAR:PutConsole (. `: '))
(iterate (= PathScanP Solution:Path)
(~= PathScanP EmptyPath)
(= PathScanP PathScanP:Next)
(;; (PAR:PutConsoleNatural (coerce natural PathScanP:Move)) (PAR:PutConsoleSpace)
);;
)iterate
(PAR:PutConsoleNewline)
(PAR:PutConsole (. `Total solution time in seconds: ')) (PAR:PutConsoleNatural ElapsedTime) (PAR:PutConsoleNewline)
);;
)critical
);;
)local
)action
)define
Маленькая книга семафоров, которая является свободно доступной книгой, имеет множество головоломок для синхронизации. Он включает в себя почти все головоломки, процитированные в других ответах. Решения предоставляются для всех головоломок.
Вот первая проблема, которую я когда-либо решал с многопоточностью, еще во время обучения в университете.