Panduan: Membuat Aplikasi Berbasis Agen

Topik ini menjelaskan cara membuat aplikasi berbasis agen dasar. Dalam panduan ini, Anda dapat membuat agen yang membaca data dari file teks secara asinkron. Aplikasi ini menggunakan algoritma checksum Adler-32 untuk menghitung checksum konten file tersebut.

Prasyarat

Anda harus memahami topik berikut untuk menyelesaikan panduan ini:

Bagian

Panduan ini menunjukkan cara melakukan tugas berikut:

Membuat Aplikasi Konsol

Bagian ini menunjukkan cara membuat aplikasi konsol C++ yang mereferensikan file header yang akan digunakan program. Langkah-langkah awal bervariasi tergantung pada versi Visual Studio mana yang Anda gunakan. Untuk melihat dokumentasi untuk versi Visual Studio pilihan Anda, gunakan kontrol pemilih Versi. Kontrol tersebut dapat ditemukan di bagian atas daftar isi pada halaman ini.

Untuk membuat aplikasi konsol C++ di Visual Studio

  1. Dari menu utama, pilih File>Baru>Proyek untuk membuka kotak dialog Buat Proyek Baru.

  2. Di bagian atas dialog, atur Bahasa ke C++, atur Platform ke Windows, dan atur jenis Project ke Konsol.

  3. Dari daftar jenis proyek yang difilter, pilih Aplikasi Konsol lalu pilih Berikutnya. Di halaman berikutnya, masukkan BasicAgent sebagai nama untuk proyek, dan tentukan lokasi proyek jika diinginkan.

  4. Pilih tombol Buat untuk membuat proyek.

  5. Klik kanan simpul proyek di Penjelajah Solusi, dan pilih Properti. Di bawah Properti>Konfigurasi C/C++>Header>yang Telah Dikompilasi sebelumnya, pilih Buat.

Untuk membuat aplikasi konsol C++ di Visual Studio 2017 dan yang lebih lama

  1. Pada menu File , klik Baru, lalu klik Proyek untuk menampilkan kotak dialog Proyek Baru.

  2. Dalam kotak dialog Proyek Baru, pilih simpul Visual C++ di panel Jenis proyek lalu pilih Aplikasi Konsol Win32 di panel Templat. Ketik nama untuk proyek, misalnya, BasicAgent, lalu klik OK untuk menampilkan Panduan Aplikasi Konsol Win32.

  3. Dalam kotak dialog Panduan Aplikasi Konsol Win32, klik Selesai.

Memperbarui file header

Dalam file pch.h (stdafx.h di Visual Studio 2017 dan yang lebih lama), tambahkan kode berikut:

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

File header agents.h berisi fungsionalitas kelas konkurensi::agent .

Memverifikasi aplikasi

Terakhir, verifikasi bahwa aplikasi berhasil dibuat dengan membangun dan menjalankannya. Untuk membuat aplikasi, pada menu Build , klik Bangun Solusi. Jika aplikasi berhasil dibangun, jalankan aplikasi dengan mengklik Mulai Penelusuran Kesalahan pada menu Debug .

[Atas]

Membuat Kelas file_reader

Bagian ini menunjukkan cara membuat file_reader kelas. Runtime menjadwalkan setiap agen untuk melakukan pekerjaan dalam konteksnya sendiri. Oleh karena itu, Anda dapat membuat agen yang melakukan pekerjaan secara sinkron, tetapi berinteraksi dengan komponen lain secara asinkron. Kelas membaca data dari file input tertentu dan mengirim data dari file tersebut file_reader ke komponen target tertentu.

Untuk membuat kelas file_reader

  1. Tambahkan file header C++ baru ke proyek Anda. Untuk melakukannya, klik kanan simpul File Header di Penjelajah Solusi, klik Tambahkan, lalu klik Item Baru. Di panel Templat, pilih File Header (.h). Dalam kotak dialog Tambahkan Item Baru, ketik file_reader.h di kotak Nama lalu klik Tambahkan.

  2. Di file_reader.h, tambahkan kode berikut.

    #pragma once
    
  3. Di file_reader.h, buat kelas yang diberi nama file_reader yang berasal dari agent.

    class file_reader : public concurrency::agent
    {
    public:
    protected:
    private:
    };
    
  4. Tambahkan anggota data berikut ke bagian private kelas Anda.

    std::string _file_name;
    concurrency::ITarget<std::string>& _target;
    concurrency::overwrite_buffer<std::exception> _error;
    

    Anggota _file_name adalah nama file yang dibaca agen. Anggota _target adalah objek konkurensi::ITarget tempat agen menulis konten file. Anggota _error menyimpan kesalahan apa pun yang terjadi selama masa pakai agen.

  5. Tambahkan kode berikut untuk file_reader konstruktor ke bagian publicfile_reader kelas.

    explicit file_reader(const std::string& file_name, 
       concurrency::ITarget<std::string>& target)
       : _file_name(file_name)
       , _target(target)
    {
    }
    
    explicit file_reader(const std::string& file_name, 
       concurrency::ITarget<std::string>& target,
       concurrency::Scheduler& scheduler)
       : agent(scheduler)
       , _file_name(file_name)
       , _target(target)
    {
    }
    
    explicit file_reader(const std::string& file_name, 
       concurrency::ITarget<std::string>& target,
       concurrency::ScheduleGroup& group)
       : agent(group) 
       , _file_name(file_name)
       , _target(target)
    {
    }
    

    Setiap kelebihan beban konstruktor mengatur file_reader anggota data. Kelebihan beban konstruktor kedua dan ketiga memungkinkan aplikasi Anda menggunakan penjadwal tertentu dengan agen Anda. Kelebihan beban pertama menggunakan penjadwal default dengan agen Anda.

  6. get_error Tambahkan metode ke bagian publik kelasfile_reader.

    bool get_error(std::exception& e)
    {
       return try_receive(_error, e);
    }
    

    Metode ini get_error mengambil kesalahan apa pun yang terjadi selama masa pakai agen.

  7. Terapkan metode konkurensi::agent::run di bagian protected kelas Anda.

    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);
          
          // Read the contents of the file and send the contents
          // to the target.
          while (fgets(buf, sizeof buf, stream))
          {
             asend(_target, std::string(buf));
          }   
          
          // 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();
    }
    

Metode run ini membuka file dan membaca data darinya. Metode ini run menggunakan penanganan pengecualian untuk menangkap kesalahan apa pun yang terjadi selama pemrosesan file.

Setiap kali metode ini membaca data dari file, metode ini memanggil fungsi konkurensi::asend untuk mengirim data tersebut ke buffer target. Ini mengirimkan string kosong ke buffer targetnya untuk menunjukkan akhir pemrosesan.

Contoh berikut menunjukkan konten lengkap file_reader.h.

#pragma once

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)
   {
   }

   explicit file_reader(const std::string& file_name, 
      concurrency::ITarget<std::string>& target,
      concurrency::Scheduler& scheduler)
      : agent(scheduler)
      , _file_name(file_name)
      , _target(target)
   {
   }

   explicit file_reader(const std::string& file_name, 
      concurrency::ITarget<std::string>& target,
      concurrency::ScheduleGroup& group)
      : agent(group) 
      , _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);
         
         // Read the contents of the file and send the contents
         // to the target.
         while (fgets(buf, sizeof buf, stream))
         {
            asend(_target, std::string(buf));
         }   
         
         // 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;
};

[Atas]

Menggunakan Kelas file_reader di Aplikasi

Bagian ini memperlihatkan cara menggunakan file_reader kelas untuk membaca konten file teks. Ini juga menunjukkan cara membuat objek konkurensi::panggilan yang menerima data file ini dan menghitung checksum Adler-32-nya.

Untuk menggunakan kelas file_reader di aplikasi Anda

  1. Di BasicAgent.cpp, tambahkan pernyataan berikut #include .

    #include "file_reader.h"
    
  2. Di BasicAgent.cpp, tambahkan arahan berikut using .

    using namespace concurrency;
    using namespace std;
    
  3. _tmain Dalam fungsi , buat konkurensi::objek peristiwa yang menandakan akhir pemrosesan.

    event e;
    
  4. Buat call objek yang memperbarui checksum saat menerima data.

    // 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(begin(s), end(s), [&] (char c) {
          a = (a + c) % 65521;
          b = (b + a) % 65521;
       });
    });
    

    Objek ini call juga mengatur event objek ketika menerima string kosong untuk memberi sinyal akhir pemrosesan.

  5. Buat file_reader objek yang membaca dari file test.txt dan menulis konten file tersebut ke call objek.

    file_reader reader("test.txt", calculate_checksum);
    
  6. Mulai agen dan tunggu hingga selesai.

    reader.start();
    agent::wait(&reader);
    
  7. Tunggu call hingga objek menerima semua data dan selesai.

    e.wait();
    
  8. Periksa pembaca file untuk kesalahan. Jika tidak ada kesalahan yang terjadi, hitung jumlah Adler-32 akhir dan cetak jumlah ke konsol.

    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;
    }
    

Contoh berikut menunjukkan file BasicAgent.cpp lengkap.

// BasicAgent.cpp : Defines the entry point for the console application.
//

#include "pch.h" // Use stdafx.h in Visual Studio 2017 and earlier
#include "file_reader.h"

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(begin(s), end(s), [&] (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;
   }
}

[Atas]

Input Sampel

Ini adalah contoh konten file input text.txt:

The quick brown fox
jumps
over the lazy dog

Output sampel

Ketika digunakan dengan input sampel, program ini menghasilkan output berikut:

Adler-32 sum is fefb0d75

Pemrograman yang Kuat

Untuk mencegah akses bersamaan ke anggota data, kami sarankan Anda menambahkan metode yang melakukan pekerjaan ke protected bagian atau private kelas Anda. Hanya tambahkan metode yang mengirim atau menerima pesan ke atau dari agen ke bagian public kelas Anda.

Selalu panggil metode concurrency::agent::d one untuk memindahkan agen Anda ke status selesai. Anda biasanya memanggil metode ini sebelum anda kembali dari run metode .

Langkah berikutnya

Untuk contoh lain aplikasi berbasis agen, lihat Panduan: Menggunakan gabungan untuk Mencegah Kebuntuan.

Baca juga

Pustaka Agen Asinkron
Blok Pesan Asinkron
Fungsi Passing Pesan
Struktur Data Sinkronisasi
Panduan: Menggunakan gabungan untuk Mencegah Kebuntuan