বিস্ফোরিত হওয়ার সময় বিভ্রান্ত: asio :: io_service চালানোর পদ্ধতিটি ব্লক / অবরোধ মুক্ত করে


91

বুস্ট.অ্যাসিওর মোট শিক্ষানবিস হয়ে, আমি এতে বিভ্রান্ত হয়ে পড়েছি io_service::run()। আমি যখন এই পদ্ধতিটি ব্লক / আনলক করে কেউ আমাকে ব্যাখ্যা করতে পারে তবে আমি এটির প্রশংসা করব। নথিগুলিতে বলা হয়েছে:

run()ফাংশন ব্লক পর্যন্ত সব কাজ সমাপ্ত হয়েছে এবং কোন আরো হ্যান্ডেলার প্রেষিত করা, অথবা পর্যন্ত io_serviceথামিয়ে দেওয়া হয়েছে।

একাধিক থ্রেড থ্রেডের run()পুলটি সেট করতে ফাংশনটিকে কল io_serviceকরতে পারে যা থেকে হ্যান্ডলারগুলি কার্যকর করতে পারে। পুলটিতে অপেক্ষা করা সমস্ত থ্রেড সমতুল্য এবং io_serviceহ্যান্ডলারের জন্য অনুরোধ করতে তাদের যে কোনও একটি চয়ন করতে পারে।

run()ফাংশন থেকে একটি সাধারণ প্রস্থান বোঝায় যে io_serviceঅবজেক্টটি বন্ধ হয়ে গেছে ( stopped()ফাংশনটি সত্য করে দেয়)। পরবর্তী কল run(), run_one(), poll()বা poll_one()অবিলম্বে ফিরে আসবে, যদি না সেখানে নেবার পূর্বাধিকার কল reset()

নিম্নলিখিত বিবৃতিটির অর্থ কী?

[...] আর হ্যান্ডলারের প্রেরণ করা হবে না [...]


এর আচরণটি বোঝার চেষ্টা করার সময় io_service::run()আমি এই উদাহরণটি দেখতে পেলাম (উদাহরণ 3 এ)। এর মধ্যে, আমি পর্যবেক্ষণ করেছি যে io_service->run()ব্লক করে এবং কাজের আদেশের জন্য অপেক্ষা করে।

// WorkerThread invines io_service->run()
void WorkerThread(boost::shared_ptr<boost::asio::io_service> io_service);
void CalculateFib(size_t);

boost::shared_ptr<boost::asio::io_service> io_service(
    new boost::asio::io_service);
boost::shared_ptr<boost::asio::io_service::work> work(
   new boost::asio::io_service::work(*io_service));

// ...

boost::thread_group worker_threads;
for(int x = 0; x < 2; ++x)
{
  worker_threads.create_thread(boost::bind(&WorkerThread, io_service));
}

io_service->post( boost::bind(CalculateFib, 3));
io_service->post( boost::bind(CalculateFib, 4));
io_service->post( boost::bind(CalculateFib, 5));

work.reset();
worker_threads.join_all();

যাইহোক, নিম্নলিখিত কোডটিতে আমি কাজ করছিলাম, ক্লায়েন্ট টিসিপি / আইপি এবং রান মেথড ব্লকগুলি ব্যবহার করে সংযুক্ত হয় যতক্ষণ না ডেটা অবিচ্ছিন্নভাবে প্রাপ্ত হয়।

typedef boost::asio::ip::tcp tcp;
boost::shared_ptr<boost::asio::io_service> io_service(
    new boost::asio::io_service);
boost::shared_ptr<tcp::socket> socket(new tcp::socket(*io_service));

// Connect to 127.0.0.1:9100.
tcp::resolver resolver(*io_service);
tcp::resolver::query query("127.0.0.1", 
                           boost::lexical_cast< std::string >(9100));
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
socket->connect(endpoint_iterator->endpoint());

// Just blocks here until a message is received.
socket->async_receive(boost::asio::buffer(buf_client, 3000), 0,
                      ClientReceiveEvent);
io_service->run();

// Write response.
boost::system::error_code ignored_error;
std::cout << "Sending message \n";
boost::asio::write(*socket, boost::asio::buffer("some data"), ignored_error);

run()নীচের দুটি উদাহরণে এর আচরণের বর্ণনা করে এমন কোনও ব্যাখ্যা প্রশংসিত হবে।

উত্তর:


238

ফাউন্ডেশন

সরলীকৃত উদাহরণ দিয়ে শুরু করুন এবং প্রাসঙ্গিক বুস্টটি পরীক্ষা করুন Aএসিও টুকরা:

void handle_async_receive(...) { ... }
void print() { ... }

...  

boost::asio::io_service io_service;
boost::asio::ip::tcp::socket socket(io_service);

...

io_service.post(&print);                             // 1
socket.connect(endpoint);                            // 2
socket.async_receive(buffer, &handle_async_receive); // 3
io_service.post(&print);                             // 4
io_service.run();                                    // 5

হ্যান্ডলার কী ?

একটি হ্যান্ডলার কলব্যাক ছাড়া আর কিছুই নয়। উদাহরণ কোডে 3 টি হ্যান্ডলার রয়েছে:

  • printহ্যান্ডলার (1)।
  • handle_async_receiveহ্যান্ডলার (3)।
  • printহ্যান্ডলার (4)।

একই print()ফাংশনটি দু'বার ব্যবহৃত হলেও প্রতিটি ব্যবহার তার নিজস্ব স্বতন্ত্র সনাক্তকরণযোগ্য হ্যান্ডলার তৈরি করতে বিবেচিত হয়। হ্যান্ডলারগুলি অনেকগুলি আকার এবং আকারে আসতে পারে, উপরের মতো মৌলিক ফাংশন থেকে শুরু করে আরও জটিল নির্মাণ যেমন ল্যাম্বডাস থেকে উত্পন্ন ফান্টেক্টর এবং আরও জটিল নির্মান থেকে শুরু করে boost::bind()। জটিলতা নির্বিশেষে, হ্যান্ডলার এখনও একটি কলব্যাক ছাড়া আর কিছুই অবশিষ্ট নেই।

কাজ কি ?

কাজটি এমন কিছু প্রক্রিয়াজাতকরণ যা বুস্ট.অ্যাসিওর কাছে আবেদন কোডের পক্ষে অনুরোধ করা হয়েছিল। কখনও কখনও বুস্ট.অ্যাসিও এটি সম্পর্কে কিছু বলা হওয়ার সাথে সাথে কিছু কাজ শুরু করতে পারে এবং অন্যান্য সময় এটি পরবর্তী সময়ে সময়ে কাজটি করার জন্য অপেক্ষা করতে পারে। কাজটি শেষ হয়ে গেলে, বুস্ট.এইসিও সরবরাহকৃত হ্যান্ডলারের সাহায্যে আবেদনটি জানিয়ে দেবে ।

Boost.Asio গ্যারান্টী বা নিশ্চয়তা হ্যান্ডেলার শুধুমাত্র একটি থ্রেড যে বর্তমানে কল করছে মধ্যে চালানো হবে run(), run_one(), poll(), অথবা poll_one()। এই থ্রেডগুলি যা কাজ করবে এবং কল করবে হ্যান্ডলারগুলি । অতএব, উপরের উদাহরণে, print()এটি io_service(1) এ পোস্ট করার সময় অনুরোধ করা হয় না । পরিবর্তে, এটিতে যুক্ত করা হয় io_serviceএবং পরবর্তী সময়ে একটি অনুরোধ করা হবে। এই ক্ষেত্রে এটি io_service.run()(5) এর মধ্যে রয়েছে।

অ্যাসিঙ্ক্রোনাস অপারেশনস কি?

একটি অ্যাসিঙ্ক্রোনাস অপারেশন কাজ তৈরি করে এবং বুস্ট.আসিয়াও কাজটি শেষ হয়ে গেলে অ্যাপ্লিকেশনটিকে জানাতে একটি হ্যান্ডলারকে অনুরোধ করবে । উপসর্গের সাথে একটি নাম রয়েছে এমন একটি ফাংশন কল করে অ্যাসিঙ্ক্রোনাস অপারেশন তৈরি করা হয় async_। এই ফাংশনগুলি সূচনা কার্য হিসাবেও পরিচিত ।

অ্যাসিনক্রোনাস অপারেশনগুলি তিনটি অনন্য পদক্ষেপে পচে যেতে পারে:

  • সূচনা করা বা অবহিত করা, সম্পর্কিত io_serviceযে কাজ করে তা করা দরকার। async_receiveঅপারেশন (3) জানায় io_serviceএটি সকেট থেকে অ্যাসিঙ্ক্রোনাস পড়া ডেটা করতে হবে, তারপর async_receiveঅবিলম্বে ফেরৎ।
  • আসল কাজ করছেন। এই ক্ষেত্রে, socketডেটা গ্রহণ করার সময়, বাইটগুলি পড়বে এবং এতে অনুলিপি হবে buffer। আসল কাজ দুটিতেই করা হবে:
    • সূচনা ফাংশন (3), যদি বুস্ট.এসিও নির্ধারণ করতে পারে যে এটি ব্লক করবে না।
    • যখন অ্যাপ্লিকেশন স্পষ্টভাবে চালিত io_service(5)।
  • Invoking handle_async_receive ReadHandler । আবার, হ্যান্ডলারগুলি কেবল চালিত থ্রেডের মধ্যেই ডাকা হবে io_service। সুতরাং, কাজটি কখন (3 বা 5) হয় তা নির্বিশেষে, এটি গ্যারান্টিযুক্ত যে handle_async_receive()কেবলমাত্র io_service.run()(5) এর মধ্যেই ডাকা হবে ।

এই তিনটি ধাপের মধ্যে সময় এবং স্থানের বিভাজন নিয়ন্ত্রণ ফ্লো বিপরীত হিসাবে পরিচিত। এটি একটি জটিলতা যা অ্যাসিক্রোনাস প্রোগ্রামিংকে কঠিন করে তোলে। তবে এমন কিছু কৌশল রয়েছে যা এটিকে প্রশমিত করতে সহায়তা করতে পারে যেমন কর্টিন ব্যবহার করে ।

কি করে io_service.run()?

যখন কোনও থ্রেড কল করবে io_service.run(), তখন এই থ্রেডের মধ্যে থেকে কাজ এবং হ্যান্ডলারগুলি ডাকা হবে। উপরের উদাহরণে, io_service.run()(5) হয় অবধি ব্লক করবে:

  • এটি উভয় printহ্যান্ডলারের কাছ থেকে ফিরে এসেছে এবং ফিরে এসেছে , গ্রহণের ক্রিয়াটি সাফল্য বা ব্যর্থতার সাথে পরিপূর্ণ হয় এবং এর handle_async_receiveহ্যান্ডলারটি আহ্বান জানানো এবং ফিরে আসে।
  • io_serviceমাধ্যমে স্পষ্টভাবে বন্ধ থাকে io_service::stop()
  • হ্যান্ডলারের মধ্যে থেকে একটি ব্যতিক্রম ছুঁড়ে দেওয়া হয়।

একটি সম্ভাব্য psuedo-ish প্রবাহ নিম্নলিখিত হিসাবে বর্ণনা করা যেতে পারে:

io_service তৈরি করুন
সকেট তৈরি
io_service এ মুদ্রণ হ্যান্ডলার যুক্ত করুন (1)
সকেটের সংযোগের জন্য অপেক্ষা করুন (2)
io_service (3) এ একটি অ্যাসিঙ্ক্রোনাস পঠন কাজের অনুরোধ যুক্ত করুন
io_service এ মুদ্রণ হ্যান্ডলার যুক্ত করুন (4)
io_service চালান (5)
  কাজ আছে নাকি হ্যান্ডলাররা?
    হ্যাঁ, এখানে 1 টি কাজ এবং 2 জন হ্যান্ডলার রয়েছে
      সকেটের কি ডেটা আছে? না, কিছু করবেন না
      প্রিন্ট হ্যান্ডলার চালান (1)
  কাজ আছে নাকি হ্যান্ডলাররা?
    হ্যাঁ, এখানে 1 টি কাজ এবং 1 হ্যান্ডলার রয়েছে
      সকেটের কি ডেটা আছে? না, কিছু করবেন না
      প্রিন্ট হ্যান্ডলার চালান (4)
  কাজ আছে নাকি হ্যান্ডলাররা?
    হ্যাঁ, 1 টি কাজ আছে
      সকেটের কি ডেটা আছে? না, অপেক্ষা অবিরত
  - সকেট তথ্য গ্রহণ করে -
      সকেটের ডেটা রয়েছে, এটি বাফারে পড়ুন
      io_service- এ হ্যান্ডেল_সেনসি_র রিসিভ হ্যান্ডলার যুক্ত করুন
  কাজ আছে নাকি হ্যান্ডলাররা?
    হ্যাঁ, এখানে 1 জন হ্যান্ডলার রয়েছে
      চালান হ্যান্ডেল_সায়েন্স_প্রাপ্ত হ্যান্ডলার (3)
  কাজ আছে নাকি হ্যান্ডলাররা?
    না, io_service সেট করে থামিয়ে দিয়ে ফিরে আসুন

পড়ার সমাপ্তিটি কীভাবে লক্ষ্য করুন, এটিতে অন্য একটি হ্যান্ডলার যুক্ত হয়েছে io_service। এই সূক্ষ্ম বিশদটি অ্যাসিক্রোনাস প্রোগ্রামিংয়ের একটি গুরুত্বপূর্ণ বৈশিষ্ট্য। এটি হ্যান্ডলারের একসাথে বেঁধে দেওয়া যায়। উদাহরণস্বরূপ, যদি handle_async_receiveএটি প্রত্যাশিত সমস্ত ডেটা না পেয়ে থাকে, তবে এর বাস্তবায়নটি আরও একটি অ্যাসিনক্রোনাস রিড অপারেশন পোস্ট করতে পারে যার ফলশ্রুতিতে io_serviceআরও বেশি কাজ রয়েছে এবং এভাবে ফিরে আসে না io_service.run()

মনে রাখবেন যে যখন কি io_serviceকাজ পরিমাণ স্বল্প আছে, আবেদন অবশ্যই আবার চালানোর আগে।reset()io_service


উদাহরণ প্রশ্ন এবং উদাহরণ 3a কোড

এখন, প্রশ্নে উল্লেখ করা কোডের দুটি টুকরো পরীক্ষা করতে দিন।

প্রশ্ন কোড

socket->async_receiveকাজ যোগ করুন io_service। সুতরাং, io_service->run()পঠন অপারেশনটি সাফল্য বা ত্রুটির সাথে সম্পূর্ণ না হওয়া অবধি অবরুদ্ধ হবে এবং ClientReceiveEventএটি চলমান শেষ করে ফেলেছে বা একটি ব্যতিক্রম ছুঁড়েছে।

উদাহরণ 3a কোড

এটি বোঝা সহজ করার আশায়, এখানে একটি ছোট এনোটোটেড উদাহরণ 3 এ:

void CalculateFib(std::size_t n);

int main()
{
  boost::asio::io_service io_service;
  boost::optional<boost::asio::io_service::work> work =       // '. 1
      boost::in_place(boost::ref(io_service));                // .'

  boost::thread_group worker_threads;                         // -.
  for(int x = 0; x < 2; ++x)                                  //   :
  {                                                           //   '.
    worker_threads.create_thread(                             //     :- 2
      boost::bind(&boost::asio::io_service::run, &io_service) //   .'
    );                                                        //   :
  }                                                           // -'

  io_service.post(boost::bind(CalculateFib, 3));              // '.
  io_service.post(boost::bind(CalculateFib, 4));              //   :- 3
  io_service.post(boost::bind(CalculateFib, 5));              // .'

  work = boost::none;                                         // 4
  worker_threads.join_all();                                  // 5
}

একটি উচ্চ-স্তরে, প্রোগ্রামটি 2 টি থ্রেড তৈরি করবে যা io_serviceইভেন্টটির লুপ (2) প্রসেস করবে । এটি একটি সাধারণ থ্রেড পুলের ফলস্বরূপ যা ফিবোনাচি সংখ্যা (3) গণনা করবে।

প্রশ্ন কোড এবং এই কোডের মধ্যে একটি প্রধান পার্থক্য হ'ল এই কোডটি প্রকৃত কাজ এবং হ্যান্ডলারের (3) এ যুক্ত হওয়ার আগেio_service::run() (2) ডাকে । অবিলম্বে প্রত্যাবর্তন থেকে রোধ করতে একটি বস্তু তৈরি করা হয় (1) এই অবজেক্টটি কাজ শেষ হতে বাধা দেয় ; অতএব, কোনও কাজের ফলে ফিরে আসবে না।io_serviceio_service::run()io_service::workio_serviceio_service::run()

সামগ্রিক প্রবাহ নিম্নরূপ:

  1. io_service::workযুক্ত করা বস্তুটি তৈরি করুন এবং যুক্ত করুন io_service
  2. থ্রেড পুল যে পূজা নির্মিত io_service::run()। এই কর্মী থ্রেডগুলি বস্তুর io_serviceকারণে ফিরে আসবে না io_service::work
  3. 3 টি হ্যান্ডলার যুক্ত করুন যা ফিবোনাচি নম্বরগুলিতে গণনা করে io_serviceএবং তত্ক্ষণাত্ ফিরে আসে। শ্রমিকের থ্রেডগুলি, মূল থ্রেড নয়, এই হ্যান্ডলারগুলি অবিলম্বে চালানো শুরু করতে পারে।
  4. io_service::workবস্তুটি মুছুন ।
  5. কর্মীদের থ্রেড চলমান শেষ হওয়ার জন্য অপেক্ষা করুন। এটি কেবলমাত্র 3 টি হ্যান্ডলারের মৃত্যুদন্ড কার্যকর করার পরে ঘটবে, কারণ io_serviceউভয়টিরই হ্যান্ডলার বা কাজ নেই।

কোডটি অন্যভাবে লিখিত হতে পারে, মূল কোড হিসাবে একই পদ্ধতিতে, যেখানে হ্যান্ডলারগুলি যুক্ত করা হয় io_serviceএবং তারপরে io_serviceইভেন্ট লুপটি প্রক্রিয়া করা হয়। এটি ব্যবহারের প্রয়োজনীয়তা io_service::workএবং নিম্নলিখিত কোডে ফলাফলগুলি সরিয়ে দেয় :

int main()
{
  boost::asio::io_service io_service;

  io_service.post(boost::bind(CalculateFib, 3));              // '.
  io_service.post(boost::bind(CalculateFib, 4));              //   :- 3
  io_service.post(boost::bind(CalculateFib, 5));              // .'

  boost::thread_group worker_threads;                         // -.
  for(int x = 0; x < 2; ++x)                                  //   :
  {                                                           //   '.
    worker_threads.create_thread(                             //     :- 2
      boost::bind(&boost::asio::io_service::run, &io_service) //   .'
    );                                                        //   :
  }                                                           // -'
  worker_threads.join_all();                                  // 5
}

সিঙ্ক্রোনাস বনাম অ্যাসিনক্রোনাস

যদিও প্রশ্নের কোডটি একটি অ্যাসিনক্রোনাস অপারেশন ব্যবহার করছে, এটি কার্যকরভাবে সিঙ্ক্রোনাসের সাথে কাজ করছে, কারণ এটি অ্যাসিনক্রোনাস অপারেশনটি সম্পন্ন হওয়ার জন্য অপেক্ষা করছে:

socket.async_receive(buffer, handler)
io_service.run();

সমান:

boost::asio::error_code error;
std::size_t bytes_transferred = socket.receive(buffer, 0, error);
handler(error, bytes_transferred);

থাম্বের একটি সাধারণ নিয়ম হিসাবে, সিঙ্ক্রোনাস এবং অ্যাসিনক্রোনাস অপারেশনগুলি মিশ্রণ এড়াতে চেষ্টা করুন। প্রায়শই, এটি একটি জটিল ব্যবস্থাকে একটি জটিল ব্যবস্থায় পরিণত করতে পারে। এই উত্তরটি অ্যাসিক্রোনাস প্রোগ্রামিংয়ের সুবিধাগুলি তুলে ধরেছে যার কয়েকটি বুস্ট.অ্যাসিও ডকুমেন্টেশনেও রয়েছে


13
দুর্দান্ত পোস্ট। আমি কেবল একটি জিনিস যুক্ত করতে চাই কারণ আমার মনে হয় এটি যথেষ্ট মনোযোগ না পেয়েছে: রান () ফিরে আসার পরে আপনি আবার () চালানোর আগে আপনার io_service এ রিসেট () কল করতে হবে। অন্যথায় এটি async_ ক্রিয়াকলাপ অপেক্ষা করছে কি না তা তাত্ক্ষণিকভাবে ফিরে আসতে পারে।
ডিভ্যাডার

বাফার কোথা থেকে আসে? এটা কি?
রুপিচেকো

আমি এখনও বিভ্রান্ত যদি মিশ্রণটি সিঙ্ক হয় এবং অ্যাসিঙ্ক প্রস্তাবিত হয় না, তবে খাঁটি অ্যাসিঙ্ক মোডটি কী? io_service.run ();) ছাড়াই আপনি কোড দেখাচ্ছে এমন উদাহরণ দিতে পারেন?
স্প্ল্যাশ

@ স্প্ল্যাশ ওয়ান io_service.poll()অসামান্য ক্রিয়াকলাপ অবরুদ্ধ না করে ইভেন্ট লুপটি প্রক্রিয়া করতে ব্যবহার করতে পারে। সিঙ্ক্রোনাস এবং অ্যাসিনক্রোনাস অপারেশনগুলিকে মিশ্রণ এড়ানোর প্রাথমিক পরামর্শটি হ'ল হ্যান্ডলারগুলি দীর্ঘ সময় নিতে গেলে অযৌক্তিক জটিলতা এড়ানো এবং দরিদ্র প্রতিক্রিয়া প্রতিরোধ করা। এটি নিরাপদ কিছু ক্ষেত্রে রয়েছে যেমন যখন কেউ জানেন যে সিঙ্ক্রোনাস অপারেশন অবরুদ্ধ হবে না।
ট্যানার সানসবারি

আপনি "বর্তমানে" দ্বারা কি বোঝাতে চেয়েছেন "Boost.Asio গ্যারান্টী যে হ্যান্ডেলার শুধুমাত্র চালানো হবে একটি থ্রেড যে বর্তমানে কল করছে মধ্যেrun() ...." ? যদি এন থ্রেড থাকে (যা ডাকে run()), তবে কোনটি "কারেন্ট" থ্রেড? অনেক থাকতে পারে? বা আপনি কি বোঝাচ্ছেন যে থ্রেডটি async_*()(বলুন async_read) সম্পাদন শেষ করেছে , তার হ্যান্ডলারগুলিকেও কল করার নিশ্চয়তা দেওয়া আছে?
নওয়াজ

19

কী runকরে তা সরল করতে , এটিকে একজন কর্মচারী হিসাবে ভাবেন যা অবশ্যই কাগজের একটি গাদা প্রক্রিয়া করে; এটি একটি শীট নেয়, শীট যা বলে তা করে, শীটটি ফেলে দেয় এবং পরেরটি নেয়; যখন সে শীট শেষ করে, অফিস থেকে বেরিয়ে যায়। প্রতিটি শীটে কোনও ধরণের নির্দেশনা থাকতে পারে, এমনকি গাদাতে একটি নতুন শীট যুক্ত করা যায়। Asio ফিরে যান: যদি আপনি একটি দিতে পারে io_serviceমূলত দুইভাবে কাজ: ব্যবহার করে postনমুনা আপনি লিঙ্ক, ইন যেমন বা অন্যান্য বস্তু যা অভ্যন্তরীণভাবে কল ব্যবহার করে postউপর io_service, মত socketও তার async_*পদ্ধতি।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.