concurrency runtime学习笔记之三:异步

先看下构架,异步代理库(Agents Library)的位置和并行库相似:

 

 异步编程模式用的是数据流,数据只有在可用的情况下才会被处理,这和习惯使用的控制流有很大区别。数据流就必然会用到消息传递函数和消息块,比方说A->B,A和B就是消息块,用来发送或接受消息,AB之间的通信由消息传递函数来完成。异步代理把任务调度有关操作封装成类,并定义有一系列生存状态,如下图所示,实线表示由用户调用,虚线表示由运行时调用。 自定义异步代理需要继承Concurrency::agent类并重写run函数。

agent life cycle

 

看下面MSDN提供的例子。例程从文件中读取数据,并用消息传递函数Concurrency::asend异步发送到消息块Concurrency::call,并在消息块中计算文件的校验值。调用Concurrency::call会另发起一个线程,这保证了主线程上的读取操作的通畅。可以这么理解,通过把计算移到辅助线程,来达到主线程的异步。理论上可能会发生文件读完了,但是计算还没有结束的情况,所以这里用Concurrency::event标记计算结束。有一点要注意,异步编程一般先搭建消息块网络,搭建方式从终端到始端,然后再传递消息,这样才能保证数据不丢失。

#include <agents.h>
#include <string>
#include <iostream>
#include <algorithm>


class file_reader : public Concurrency::agent
{
public:
    explicit file_reader(const std::string& file_name, 
        Concurrency::ITarget<std::string>& target)
        : _file_name(file_name)
        , _target(target)
    {
    }


    // Retrieves any error that occurs during the life of the agent.
    bool get_error(std::exception& e)
    {
        return try_receive(_error, e);
    }

protected:
    void run()
    {
        FILE* stream;
        try
        {
            // Open the file.
            if (fopen_s(&stream, _file_name.c_str(), "r") != 0)
            {
                // Throw an exception if an error occurs.            
                throw std::exception("Failed to open input file.");
            }

            // Create a buffer to hold file data.
            char buf[1024];

            // Set the buffer size.
            setvbuf(stream, buf, _IOFBF, sizeof buf);

            std::ostream_iterator<std::string> ofile(std::cout,"");
            std::list<std::string> text;

            // Read the contents of the file and send the contents
            // to the target.
            while (fgets(buf, sizeof buf, stream))
            {
                text.push_back(buf);
                asend(_target, std::string(buf));
            }

            std::copy(text.begin(),text.end(),ofile);
            std::cout << "\n\n";

            // Send the empty string to the target to indicate the end of processing.
            asend(_target, std::string(""));

            // Close the file.
            fclose(stream);
        }
        catch (const std::exception& e)
        {
            // Send the empty string to the target to indicate the end of processing.
            asend(_target, std::string(""));

            // Write the exception to the error buffer.
            send(_error, e);
        }

        // Set the status of the agent to agent_done.
        done();
    }

private:
    std::string _file_name;
    Concurrency::ITarget<std::string>& _target;
    Concurrency::overwrite_buffer<std::exception> _error;
};


using namespace Concurrency;
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
   // An event object that signals the end of processing.
   event e;

   // The components of the Adler-32 sum.
   unsigned int a = 1;
   unsigned int b = 0;

   // A call object that updates the checksum when it receives data.
   call<string> calculate_checksum([&] (string s) {
      // If the input string is empty, set the event to signal
      // the end of processing.
      if (s.size() == 0)
         e.set();
      // Perform the Adler-32 checksum algorithm.
      for_each(s.begin(), s.end(), [&] (char c) {
         a = (a + c) % 65521;
         b = (b + a) % 65521;
      });
   });

   // Create the agent.
   file_reader reader("test.txt", calculate_checksum);

   // Start the agent and wait for it to complete.
   reader.start();
   agent::wait(&reader);

   // Wait for the call object to receive all data and complete.
   e.wait();

   // Check the file reader for errors.
   // If no error occurred, calculate the final Adler-32 sum and print it 
   // to the console.
   std::exception error;
   if (reader.get_error(error))
   {
      wcout << error.what() << endl;
   }   
   else
   {      
      unsigned int adler32_sum = (b << 16) | a;
      wcout << L"Adler-32 sum is " << hex << adler32_sum << endl;
   }
}

 

我们也可以不用这么麻烦去自定义异步代理,只用一个消息块就行,比方说这样:

#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // An event that is set when the call object receives all values.
   event received_all;

   // Counts the 
   long receive_count = 0L;
   long max_receive_count = 3L;

   // Create an call object that works with int data.
   call<int> target([&received_all,&receive_count,max_receive_count](int n) {
      // Print the value that the call object receives to the console.
      wcout << n << endl;

      // Set the event when all messages have been processed.
      if (++receive_count == max_receive_count)
         received_all.set();
   });

   // Send a few items to the call object.
   send(target, 33);
   send(target, 44);
   send(target, 55);

   // Wait for the call object to process all items.
   received_all.wait();
}

 

MSDN上还有更多演示,例如把工作线程和UI线程分开提高响应度,用异步消息块实现图像处理算法等等,有兴趣的同学可以看下。

http://msdn.microsoft.com/zh-cn/library/dd997846.aspx

posted @ 2012-04-25 20:07  richfox  阅读(1769)  评论(2编辑  收藏  举报