2

I have 3 classes of task (I, D, U) which come in on a queue, tasks of the same class must be processed in order. I want tasks to run as concurrently as possible; however there are some constraints:

  • U and D cannot run concurrently
  • U and I cannot run concurrently
  • I(n) requires U(n) has completed

Q: What design pattern(s) would fit this class of problem?

I have two approaches I am considering:

Approach 1: Use 1 Thread per task, each with its own queue. Each thread has a synchronized start phase where it checks start conditions, then runs, then a synchronized stop phase. It is easy to see that this will provide good concurrency but I am unsure if it correctly implements my constraints and doesnt deadlock.

D_Thread { ...
 while (task = D_Queue.take()) {
  synchronized (State) {   // start phase
   waitForU();
   State.setRunning(D, true);
  }
  run(task);  // run phase
  synchronized (State) {   // stop phase
    State.setRunning(D, false) 
  }
 }
}

Approach 2: Alternatively, a single dispatch thread manages execution state, and schedules tasks in a ThreadPool, waiting if necessary for currently scheduled tasks to complete.

Justin
  • 4,437
  • 6
  • 32
  • 52
  • Could you elaborate more on third constraint? Do I and U come in pairs and each I requires its paired U, or there are no such pairs but merely the number of completed I's can not be greater than the numbers of completed U's? Or something else? – Dialecticus Nov 18 '10 at 21:37
  • 2) What is the time cost for each class of tasks? Do they take approximately the same time to complete? – Dialecticus Nov 18 '10 at 21:52
  • A bit of both. U(n) is kind of a cache flush, it implies U(n-1) U(n-2) ... and so on; so its possible to process several at once. Each operation has a position associated with it; so D(k) actually creates a requirement for U(k). They were meant as examples; I'm not asking people to fully solve this problem; just recommend some design patterns. – Justin Nov 18 '10 at 21:56

2 Answers2

1

The Objective-C Foundation framework includes classes NSOperationQueue and NSOperation that satisfy some of these requirements. NSOperationQueue represents a queue of NSOperations. The queue runs a configurable maximum number of operations concurrently. Operations have a priority and a set of dependencies; all of the operations that an operation depends on must be completed before the queue will start running the operation. The operations are scheduled to run on a dynamically-sized pool of threads.

What you need requires a somewhat smarter version of NSOperationQueue that applies the constraints you have expressed, but NSOperationQueue and company provide an example of how roughly your problem has been solved in a production framework that resembles your second suggested solution of a dispatch thread running tasks on a thread pool.

Jeremy W. Sherman
  • 35,901
  • 5
  • 77
  • 111
  • It looks like NSOperationQueue uses a directed acyclic graph of dependencies and a bounded thread pool to schedule everything which is not pending another result. – Justin Nov 18 '10 at 22:28
  • I couldn't see a way to express my constraints in terms of forward dependencies. – Justin Nov 19 '10 at 20:36
  • I'll take it, this does not solve the problem but it is what I asked. – Justin Nov 22 '10 at 16:38
0

Actually this turns out to be more simple than it seemed: a mutex is mainly all that is needed:

IThread(int k) {
 synchronized (u_mutex) {
  if (previousUSet.contains(k))) U(k);
 }
 I(k);
}

DThread(int k) {
 synchronized (u_mutex) {
  D(k);
  previousUSet.add(k);
 }
}
Justin
  • 4,437
  • 6
  • 32
  • 52