Comega-Buffer研究笔记

以下文章来自Comega文档
Buffer Tutorial

This tutorial shows how to create and use a buffer in a simple program. The program can be compiled using the Cω compiler.

Sample Files

To run this tutorial, you may use the following project and source files:

  • Buffer.cwproj
  • main.cw

These files are located in the \samples\Concurrency\Buffer subdirectory under the path where you installed Cω, which by default is C:\Program Files\Microsoft Research\Comega.

Tutorial

Here is the simplest interesting example of a Cω class that uses chords:

public class Buffer
{
   public async Put(string s);
   public string Get() & Put(string s) { return s; }
}

This class contains two methods: a synchronous one, Get(), which takes no arguments and returns a string, and an asynchronous one, Put(), which takes a string argument and (like all asynchronous methods) returns no result. The class definition contains two things: a declaration (with no body) for the asynchronous method, and a chord. The chord declares the synchronous method and defines a body (the return statement) which can run when both the Get() and Put() methods have been called.

这个类包括两个方法,Get()是同步的方法,而Put()则是异步的,这样做,可以不断地向缓冲中放入数据,而Get则按顺序一个一个地读取,Put()与Get()是相分离的两个操作。

Now assume that producer and consumer threads wish to communicate via an instance b of the class Buffer. Producers make calls to b.Put(), which, since the method is asynchronous, never block. Consumers make calls to b.Get(), which, since the method is synchronous, will block until or unless there is a matching call to Put(). Once b has received both a Put() and a Get(), the body runs and the argument to the Put() is returned as the result of the call to Get(). Multiple calls to Get() may be pending before a Put() is received to reawaken one of them, and multiple calls to Put() may be made before their arguments are consumed by subsequent Get()s. Note that:

  • The body of the chord runs in the (reawakened) thread corresponding to the matched call to Get(). Hence no new threads are spawned in this example.
  • The code which is generated by the class definition above is completely thread safe. The compiler automatically generates the locking necessary to ensure that, for example, the argument to a particular call of Put() cannot be delivered to two distinct calls to Get(). Furthermore (though it makes little difference in this small example), the locking is fine-grained and brief - polyphonic methods to not lock the whole object and are not executed with ``monitor semantics''.
  • The reader may wonder how we know which of the methods involved in a chord gets the returned value. The answer is that it is always the synchronous one, and there can be at most one synchronous method involved in a chord.

这实际上是生产者-消费者模型,在Comega中,采用&符号简单地实现了线程安全的生产-消费者模型。
&是Comega内实现线程同步的一个操作符,Get()&Put(),就可以强制性地指定在Put()后,才能Get(),内部实际上是这样的:当Put()后,内部有一个计数器就增1,而Get后,内部的计数器就减1,只有计数器>0的情况下,Get()操作才允许被进行。
Comega通过&符号,很轻易地实现了这种模式,不得不说是Comega的强大之处,这使得开发过程中,不必再采用join+计数器的方式来实现生产-消费的模型

In general, the definition of a synchronous method in Cω consists of more than one chord, each of which defines a body which can run when the method has been called and a particular set of asynchronous messages are present. For example, we could modify the example above to allow Get() to synchronize with calls to either of two different Put() methods:

public class Buffer {
  public async Put(string s);
  public async Put(int n);
  public string Get()
       & Put(string s) {  return s; }
       & Put(int n) { return n.ToString(); }
}

Now we have two asynchronous methods (which happen to be distinguished by type rather than name) and a synchronous method which can synchronize with either one, with a different body in each case.

Example

The following is a complete Cω program that declares a buffer class and two threads that concurrently Put() and Get() values from the buffer. To terminate the program, just close the output window.

using System;
using System.Threading;

public class Buffer {
   public async Put(string s);

   public string Get() & Put(string s) {
      return s;
   }
}

public class Demo {
  static Buffer b = new Buffer();

  static async Producer() {
    for(i=0; i<10; i++) {
      b.Put(i.ToString());
      Console.WriteLine("Produced!{0}", i);
      Thread.Sleep(10);
    }
  }

  static async Consumer(){
    while(true){
      string s = b.Get();
      Console.WriteLine("Consumed?{0}",s);
      Thread.Sleep(8);
    }
  }

  public static void Main() {
    Producer();
    Consumer();
  }
}

Output

The output of this program is non-deterministic and depends on the actual scheduling of the threads.

Produced!0
Produced!1
Consumed?0
Consumed?1
Produced!2
Consumed?2
Produced!3
Consumed?3
Produced!4
Produced!5
Consumed?4
Consumed?5
Produced!6
Produced!7
Consumed?6
Produced!8
Consumed?7
Consumed?8
Produced!9
Consumed?9
posted @ 2005-05-16 22:40  一根神棍研古今  阅读(598)  评论(0)    收藏  举报
Web Counter