public async Task <DPLLResultHolder> SatisfiableParallel(CNF cnf) { parallel = true; sharedModelQueue.Clear(); sharedModelQueue.Enqueue(cnf); // Initial formula (model) to solve List <Task <DPLLResultHolder> > taskList = new List <Task <DPLLResultHolder> >(); CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); var token = cancellationTokenSource.Token; var idleThreadsCounter = new IdleThreadsCounter(); for (int i = 0; i < Environment.ProcessorCount; i++) { taskList.Add(Task.Run(() => Solve(this, sharedModelQueue, idleThreadsCounter, token), token)); } var taskArray = taskList.ToArray(); var finalTask = await Task.WhenAny(taskArray); cancellationTokenSource.Cancel(); // Cancel other tasks still solving SAT lock (sharedModelQueue) { Monitor.PulseAll(sharedModelQueue); // Wake tasks waiting at the queue, so they can cancel } parallel = false; return(await finalTask); }
// Each thread's "main" function private DPLLResultHolder Solve(DPLL parallelDPLL, Queue <CNF> sharedModelQueue, IdleThreadsCounter idleThreadsCounter, CancellationToken token) { CNF model; while (true) { lock (sharedModelQueue) { while (sharedModelQueue.Count == 0) { Monitor.Wait(sharedModelQueue); // Wait if there's nothing to solve token.ThrowIfCancellationRequested(); } Interlocked.Decrement(ref idleThreadsCounter.Counter); // This task started working - is no longer idle model = sharedModelQueue.Dequeue(); // Get a CNF model from the shared queue } token.ThrowIfCancellationRequested(); DPLLResultHolder solverThreadResult = parallelDPLL.Satisfiable(model); if (solverThreadResult.SAT) { return(solverThreadResult); // The task has found SAT model } else { Interlocked.Increment(ref idleThreadsCounter.Counter); // The task is idle for now } lock (sharedModelQueue) { // If all threads are idle and the queue is empty -> we're done and result remains unsat if (Interlocked.Read(ref idleThreadsCounter.Counter) == Environment.ProcessorCount && sharedModelQueue.Count == 0) { return(solverThreadResult); } } } }