《多处理器编程的艺术》附录A提到的多线程基本问题。包括Java、C#和C(pthreads)的实现:线程创建,管程,线程局部对象和生产者消费者问题的解决(仅供参考)
一、C#版,用VS2008测试。
using System; //using System.Collections.Generic; //using System.Linq; //using System.Text; using System.Threading; //see 《多处理器编程的艺术》附录A //The Art of Multiprocessor Programming namespace t1 { //循环队列,管程锁(监视器Monitor) class Queue<T> { int head; //读出位置 int tail; //写入位置 T[] call; public Queue(int capacity) { call = new T[capacity]; head = tail = 0; } public void Enq(T x) { Monitor.Enter(this); try { while (tail - head == call.Length) { Monitor.Wait(this); //队列满 } call[(tail++) % call.Length] = x; Monitor.Pulse(this); //激活等待的出列线程 } finally { Monitor.Exit(this); } } public T Deq() { Monitor.Enter(this); try { while (tail == head) { Monitor.Wait(this); //队列满 } T y = call[(head++) % call.Length]; Monitor.Pulse(this); //激活等待的进列线程 return y; } finally { Monitor.Exit(this); } } } //Thread-Local Objects //静态域转换为本地线程对象,作为线程的唯一标识 class ThreadID { [ThreadStatic] static int myID; static int counter; public static int get() { //只有是未设置ID时(不同的线程)才加一, //如果线程已经get了一次,就不会加一 if (myID == 0) { myID = Interlocked.Increment(ref counter); } return myID - 1; } } //共享计数器,临界区 class Counter { private int value; public Counter(int i) { value = i; } //加一,返回加一前的值 public int GetAndIncrement() { lock (this) { return value++; } } } //测试主入口 class Program { static void HelloWorld() { Console.WriteLine("Hello World"); } //TODO:创建线程 static void test1() { ThreadStart hello = new ThreadStart(delegate() { Console.WriteLine("Hello World"); }); Thread thread = new Thread(hello); thread.Start(); thread.Join(); thread = new Thread(new ThreadStart(HelloWorld)); thread.Start(); thread.Join(); } //TODO:多线程同步与本地线程对象 static void test2() { Counter counter = new Counter(0); Thread[] thread = new Thread[8]; for (int i = 0; i < thread.Length; i++) { String message = "Hello world from thread" + i; ThreadStart hello = delegate() { Console.WriteLine(message); Console.WriteLine(">>>ThreadID:" + ThreadID.get() + ", and get again:" + ThreadID.get()); Console.WriteLine(">>>>>locked counter:" + counter.GetAndIncrement()); }; thread[i] = new Thread(hello); } for (int i = 0; i < thread.Length; i++) { thread[i].Start(); } //等待线程结束 for (int i = 0; i < thread.Length; i++) { thread[i].Join(); } Console.WriteLine("done!"); } //TODO:生产者-消费者问题,双线程共享一个FIFO队列 //The Producer–Consumer Problem static void test3() { Queue<int> queue = new Queue<int>(10); //默认是使用时间做随机种子 Random randomProducer = new Random(); Random randomConsumer = new Random(); ThreadStart producer = new ThreadStart(delegate() { Console.WriteLine("producer thread start"); for (int i = 0; i < 20; i++) { queue.Enq(i); Console.WriteLine("<< Producer put:" + i); Thread.Sleep(randomProducer.Next(100)); //Console.WriteLine(randomConsumer.Next(100)); } }); ThreadStart consumer = new ThreadStart(delegate() { Console.WriteLine("consumer thread start"); for (int i = 0; i < 20; i++) { int value = queue.Deq(); Console.WriteLine(">> Consumer got:" + value); Thread.Sleep(randomConsumer.Next(100)); //Console.WriteLine(randomConsumer.Next(100)); } }); //new Thread[2] Thread[] thread = {new Thread(producer), new Thread(consumer)}; for (int i = 0; i < thread.Length; i++) { thread[i].Start(); } //等待线程结束 for (int i = 0; i < thread.Length; i++) { thread[i].Join(); } Console.WriteLine("done!"); } static void Main(string[] args) { test1(); test2(); test3(); Console.ReadKey(); } } }
二、Pthreads版,C代码,用cygwin测试(未考虑free问题)
/* see The Art of Multiprocessor Programming In cygwin: > rm -f *.exe && gcc main.c && ./a.exe */ #include <stdlib.h> #include <stdio.h> #include <time.h> #include <pthread.h> #define NUM_THREADS 8 #define QSIZE 10 //----------------------------------------------- //Queue, and monitor typedef struct { int buf[QSIZE]; long head, tail; pthread_mutex_t *mutex; pthread_cond_t *notFull, *notEmpty; } queue; void queue_enq(queue* q, int item) { //or use pthread_mutex_trylock to return immediately pthread_mutex_lock(q->mutex); while(q->tail - q->head == QSIZE) { //condition variable and lock(mutex) pthread_cond_wait(q->notFull, q->mutex); } q->buf[q->tail % QSIZE] = item; q->tail++; pthread_mutex_unlock(q->mutex); //or use pthread_cond_broadcast to notify all pthread_cond_signal(q->notEmpty); } int queue_deq(queue* q) { int result; pthread_mutex_lock(q->mutex); while(q->tail == q->head) { pthread_cond_wait(q->notEmpty, q->mutex); } result = q->buf[q->head % QSIZE]; q->head++; pthread_mutex_unlock(q->mutex); pthread_cond_signal(q->notFull); return result; } queue* queue_init() { queue *q; q = (queue*)malloc(sizeof(queue)); if(q == NULL) return NULL; q->head = 0; q->tail = 0; q->mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); pthread_mutex_init(q->mutex, NULL); q->notFull = (pthread_cond_t*)malloc(sizeof(pthread_cond_t)); pthread_cond_init(q->notFull, NULL); q->notEmpty = (pthread_cond_t*)malloc(sizeof(pthread_cond_t)); pthread_cond_init(q->notEmpty, NULL); return q; } //----------------------------------------------- //Thread-Local Objects pthread_key_t key; int counter; pthread_mutex_t mutex; void threadID_init() { pthread_mutex_init(&mutex, NULL); pthread_key_create(&key, NULL); counter = 0; } int threadID_get() { int* id = (int*)pthread_getspecific(key); if(id == NULL) { id = (int *)malloc(sizeof(int)); pthread_mutex_lock(&mutex); *id = counter++; pthread_setspecific(key, id); pthread_mutex_unlock(&mutex); } return *id; } //----------------------------------------------- //Counter, locked typedef struct { int value; pthread_mutex_t *mutex; } locked_counter; locked_counter *lockedCounter; locked_counter* locked_counter_init() { locked_counter* c = (locked_counter*)malloc(sizeof(locked_counter)); if(c == NULL) return NULL; c->value = 0; c->mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); pthread_mutex_init(c->mutex, NULL); return c; } int getAndIncrement(locked_counter* c) { int i; pthread_mutex_lock(c->mutex); i = c->value; c->value++; pthread_mutex_unlock(c->mutex); return i; } //----------------------------------------------- //delegate //void* (*thread_function)(void*) void* helloworld(void *arg) { printf("Hello World\n"); } void* hello(void *arg) { printf("Hello from thread %d\n", (int)arg); printf(">>>ThreadID:%d, and get again:%d\n", threadID_get(), threadID_get()); printf(">>>>>locked counter:%d\n", getAndIncrement(lockedCounter)); } void* producer(void *arg) { int i; queue *q; q = (queue*)arg; //see http://www.cplusplus.com/reference/clibrary/cstdlib/rand/ srand(time(NULL)); printf("producer thread start\n"); for (i = 0; i < 20; i++) { queue_enq(q, i); printf("<< Producer put:%d\n", i); //1000 * 1000 micro seconds == 1 second //or use sleep usleep((rand() % 100) * 1000); //printf("%d\n", (rand() % 100)); } } void* consumer(void *arg) { int i; queue *q; q = (queue*)arg; //see http://www.cplusplus.com/reference/clibrary/cstdlib/rand/ srand(time(NULL) + 12345); printf("consumer thread start\n"); for (i = 0; i < 20; i++) { int value = queue_deq(q); printf(">> Consumer got:%d\n", i); usleep((rand() % 100) * 1000); //printf("%d\n", (rand() % 100)); } } //----------------------------------------------- void test1() { pthread_t thread; if(pthread_create(&thread, NULL, helloworld, NULL) != 0) { printf("pthread_create() error\n"); exit(1); } pthread_join(thread, NULL); } void test2() { pthread_t thread[NUM_THREADS]; int i; //create locked_counter lockedCounter = locked_counter_init(); if(lockedCounter == NULL) { printf("locked_counter_init() error\n"); exit(1); } // create threadID threadID_init(); for(i = 0; i < NUM_THREADS; i++) { //Create thread and start immediately if(pthread_create(&thread[i], NULL, hello, (void *)i) != 0) { printf("pthread_create() error\n"); exit(1); } } for(i = 0; i < NUM_THREADS; i++) { pthread_join(thread[i], NULL); } printf("done!\n"); } void test3() { pthread_t threadProducer, threadConsumer; queue *q; //create queue q = queue_init(); if(q == NULL) { printf("queue_init() error\n"); exit(1); } //see http://www.cplusplus.com/reference/clibrary/cstdlib/rand/ //srand(time(NULL)); if(pthread_create(&threadProducer, NULL, producer, (void *)q) != 0) { printf("pthread_create() error\n"); exit(1); } if(pthread_create(&threadConsumer, NULL, consumer, (void *)q) != 0) { printf("pthread_create() error\n"); exit(1); } pthread_join(threadProducer, NULL); pthread_join(threadConsumer, NULL); printf("done!\n"); } int main() { test1(); test2(); test3(); return 0; }
三、Java版,用Eclipse和JDK6测试(使用并发库实现的带锁循环队列没有测试)
//see 《多处理器编程的艺术》附录A //The Art of Multiprocessor Programming import java.util.Random; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; //使用wait和notifyAll的队列 class CallQueue<T> { private int head = 0; // 读出位置 private int tail = 0; // 写入位置 private T[] calls; @SuppressWarnings("unchecked") public CallQueue(int capacity) { calls = (T[]) new Object[capacity]; } public synchronized void enq(final T x) { while (tail - head == calls.length) { try { wait(); // 等待未满 } catch (InterruptedException e) { } } calls[tail] = x; if (++tail == calls.length) { tail = 0; } notifyAll(); } public synchronized T deq() { while (head == tail) { try { wait(); // 等待非空 } catch (InterruptedException e) { } } T x = calls[head]; if (++head == calls.length) { head = 0; } notifyAll(); return x; } } // 使用ReentrantLock和Condition的队列 class LockedQueue<T> { private final Lock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); private final T[] items; private int count = 0; // 长度 private int head = 0; // 读出位置 private int tail = 0; // 写入位置 @SuppressWarnings("unchecked") public LockedQueue(final int capacity) { items = (T[]) new Object[capacity]; } public void enq(T x) { lock.lock(); // 或者使用tryLock() try { while (count == items.length) { notFull.await(); // 等待未满 } items[tail] = x; if (++tail == items.length) { tail = 0; } ++count; notEmpty.signal(); // 满足非空的条件 } catch (InterruptedException e) { } finally { lock.unlock(); } } public T deq() { lock.lock(); try { while (count == 0) { notEmpty.await(); // 等待非空 } T x = items[head]; if (++head == items.length) { head = 0; } --count; notFull.signal(); // 满足未满的条件 return x; } catch (InterruptedException e) { } finally { lock.unlock(); } return null; } } // 线程局部对象 // Thread-Local Objects class ThreadID { // 只是用于ThreadID值的递增,与线程无关(属于主线程) private static volatile int nextID = 0; // 必须重写initialValue才可以使用(实例化)ThreadLocal类 private static class ThreadLocalID extends ThreadLocal<Integer> { // 每个线程对应一个ThreadID变量,而ThreadID变量间互不影响 // 用synchronized使nextID++是原子操作 // 所以每个ThreadID变量的值也不同 protected synchronized Integer initialValue() { return nextID++; } } // 虽然是static,但由于继承ThreadLocal, // 每个引用ThreadLocalID的线程看到的静态实例将是不同的对象。 // 而没有使用它的线程则不会创建它。 private static ThreadLocalID threadID = new ThreadLocalID(); public static int get() { return threadID.get(); } // 一般不需要set,而是让ThreadLocal的initialValue来修改nextID的值 public static void set(int index) { threadID.set(index); } } // 共享计数器,临界区 class Counter { private int value; public Counter(int i) { value = i; } // 加一,返回加一前的值 public int getAndIncrement() { synchronized (this) { return value++; } } } // 显式继承Runnable,而非匿名类 class HelloWorld implements Runnable { String message; public HelloWorld(String m) { message = m; } public void run() { System.out.println(message); } } // 测试主入口 public class Test { // 创建线程 public static void test1() { String m = "Hello World from thread"; Thread thread = new Thread(new HelloWorld(m)); thread.start(); try { // 阻塞直至线程thread返回 thread.join(); } catch (InterruptedException e) { } final String message = "Hello World from thread"; thread = new Thread(new Runnable() { public void run() { System.out.println(message); } }); thread.start(); try { // 阻塞直至线程thread返回 thread.join(); } catch (InterruptedException e) { } } // 多线程同步与本地线程对象 public static void test2() { Thread[] thread = new Thread[8]; final Counter counter = new Counter(0); for (int i = 0; i < thread.length; i++) { final String message = "Hello world from thread" + i; thread[i] = new Thread(new Runnable() { public void run() { System.out.println(message); System.out.println(">>>ThreadID:" + ThreadID.get() + ", and get again:" + ThreadID.get()); System.out.println(">>>>>locked counter:" + counter.getAndIncrement()); } }); } for (int i = 0; i < thread.length; i++) { thread[i].start(); } // 等待线程结束 for (int i = 0; i < thread.length; i++) { try { thread[i].join(); } catch (InterruptedException e) { } } System.out.println("done!"); } // 生产者-消费者问题,双线程共享一个FIFO队列 public static void test3() { final CallQueue<Integer> queue = new CallQueue<Integer>(10); Thread producer = new Thread(new Runnable() { public void run() { // 初始化随机种子 Random rand = new Random(System.currentTimeMillis()); System.out.println("producer thread start"); for (int i = 0; i < 20; i++) { queue.enq(i); System.out.println("<< Producer put:" + i); try { Thread.sleep(rand.nextInt(100)); // System.out.println(rand.nextInt(100)); } catch (InterruptedException e) { } } } }); Thread consumer = new Thread(new Runnable() { public void run() { // 初始化随机种子 Random rand = new Random(System.currentTimeMillis() + 12345); System.out.println("consumer thread start"); for (int i = 0; i < 20; i++) { int value = queue.deq(); System.out.println(">> Consumer got:" + value); try { Thread.sleep(rand.nextInt(100)); // System.out.println(rand.nextInt(100)); } catch (InterruptedException e) { } } } }); producer.start(); consumer.start(); // 阻塞直至线程返回 try { producer.join(); } catch (InterruptedException e) { } try { consumer.join(); } catch (InterruptedException e) { } System.out.println("done!"); } public static void main(String[] args) { test1(); test2(); test3(); } }
四、TODO:
(待续)