কীভাবে .NET এ নিয়ন্ত্রণ প্রবাহ বাস্তবায়িত এবং প্রতীক্ষিত হয়?


105

আমি yieldকীওয়ার্ডটি বুঝতে পারছি , যদি কোনও পুনরুক্তিকারী ব্লকের অভ্যন্তর থেকে ব্যবহার করা হয়, এটি কলিং কোডে নিয়ন্ত্রণের প্রবাহ ফিরিয়ে দেয় এবং যখন পুনরুক্তিকারীটিকে আবার কল করা হয়, এটি যেখানে ছেড়ে গিয়েছিল সেখানে তুলে দেয়।

এছাড়াও, awaitকেবল কলির জন্য অপেক্ষা করে না, তবে এটি কলারের কাছে নিয়ন্ত্রণ ফিরিয়ে দেয়, কেবল কলার awaitsযখন পদ্ধতিটি ছেড়েছিল তখনই এটি কোথায় গিয়েছিল pick

অন্য কথায় - কোনও থ্রেড নেই , এবং অ্যাসিঙ্ক এবং প্রতীক্ষার "একচ্ছত্রতা" হ'ল নিয়ন্ত্রণের চতুর প্রবাহের কারণে সৃষ্ট একটি মায়া, যার বিবরণটি সিনট্যাক্স দ্বারা গোপন করা হয়।

এখন, আমি একজন প্রাক্তন অ্যাসেম্বলি প্রোগ্রামার এবং আমি নির্দেশ পয়েন্টার, স্ট্যাকস ইত্যাদির সাথে খুব পরিচিত এবং আমি কীভাবে নিয়ন্ত্রণের প্রবাহ (সাব্রোটিন, রিকার্সন, লুপস, শাখা) কাজ করে তা পেয়েছি। কিন্তু এই নতুন নির্মাণ - আমি সেগুলি পাই না।

যখন একটি awaitপৌঁছে যায়, রানটাইম কীভাবে জানতে পারে যে কোডের টুকরোটি পরবর্তী কীভাবে কার্যকর করা উচিত? এটি কোথায় ছেড়ে যায় যেখানে এটি আবার শুরু করতে পারে তা কীভাবে জানবে এবং কীভাবে এটি মনে আছে? বর্তমান কল স্ট্যাকের সাথে কী ঘটে, এটি কোনওভাবে সংরক্ষণ করা যায়? যদি কল করার পদ্ধতিটি অন্য পদ্ধতি কল করার আগে কল করে await- তবে স্ট্যাকটি ওভাররাইট করা হচ্ছে না কেন? এবং পৃথিবীতে রানটাইম কীভাবে ব্যতিক্রম এবং স্ট্যাকের উদ্বোধনের ক্ষেত্রে এই সমস্ত কিছু দিয়ে কাজ করবে?

যখন yieldপৌঁছে যায়, রানটাইম কীভাবে পয়েন্টগুলি আপ করা উচিত যেখানে ট্র্যাক রাখে? পুনরুক্তি রাষ্ট্র কীভাবে সংরক্ষণ করা হয়?


4
আপনি ট্রাইরোসলিন অনলাইন সংকলকটিতে জেনারেট কোডটি একবার দেখে নিতে পারেন
xanatos

1
আপনি জোন স্কিটের দ্বারা এডুয়াসেক নিবন্ধ সিরিজ চেক করতে চাইতে পারেন ।
লিওনিড ভাসেলিভ

সম্পর্কিত আকর্ষণীয় পড়া: স্ট্যাকওভারফ্লো.com
জেসন সি

উত্তর:


115

আমি নীচে আপনার নির্দিষ্ট প্রশ্নের উত্তর দেব, তবে আমরা সম্ভবত আমাদের ফলনটি কীভাবে ডিজাইন করেছি এবং অপেক্ষা করব সে সম্পর্কে আমার বিস্তৃত নিবন্ধগুলি কেবল ভালভাবে পড়তে পারেন।

https://blogs.msdn.microsoft.com/ericlippert/tag/continuation-passing-style/

https://blogs.msdn.microsoft.com/ericlippert/tag/iterators/

https://blogs.msdn.microsoft.com/ericlippert/tag/async/

এই নিবন্ধগুলির কিছু এখন পুরানো; উত্পন্ন কোডটি অনেক উপায়ে ভিন্ন। তবে এগুলি আপনাকে কীভাবে কাজ করে তা অবশ্যই ধারণা দেবে।

এছাড়াও, যদি আপনি বুঝতে না পারছেন যে ল্যাম্বডাস কীভাবে ক্লোজার ক্লাস হিসাবে উত্পন্ন হয় তবে প্রথমে এটি বুঝতে হবে । ল্যাম্বডাস নীচে না নিলে আপনি অ্যাসিঙ্কের মাথা বা লেজ তৈরি করবেন না।

যখন একটি প্রত্যাশা পৌঁছে যায়, রানটাইম কীভাবে জানতে পারে যে কোডের টুকরোটি পরবর্তী কীভাবে কার্যকর করা উচিত?

await হিসাবে উত্পন্ন:

if (the task is not completed)
  assign a delegate which executes the remainder of the method as the continuation of the task
  return to the caller
else
  execute the remainder of the method now

মূলত এটি। ওয়েত হ'ল অভিনব রিটার্ন।

এটি কোথায় ছেড়ে যায় যেখানে এটি আবার শুরু করতে পারে তা কীভাবে জানবে এবং কীভাবে এটি মনে আছে?

ভাল, আপনি কিভাবে অপেক্ষা না করে এটি করবেন ? যখন পদ্ধতি foo পদ্ধতি বারকে কল করে, কোনওভাবে আমাদের মনে আছে কীভাবে foo এর মাঝখানে ফিরে যাব, সমস্ত স্থানীয়দের সাথে foo সক্রিয়করণের অক্ষত অক্ষুণ্ন থাকুক না কেন, বারটি যাই হোক না কেন।

আপনি জানেন কীভাবে এটি এসেম্বলারের মাধ্যমে করা হয়েছে। Foo এর জন্য একটি অ্যাক্টিভেশন রেকর্ডটি স্ট্যাকের উপরে চাপ দেওয়া হয়; এটিতে স্থানীয়দের মূল্যবোধ রয়েছে। কলের বিন্দুতে foo- এ রিটার্ন ঠিকানাটি স্ট্যাকের দিকে ঠেলা দেওয়া হয়। যখন বারটি সম্পন্ন হয়, স্ট্যাক পয়েন্টার এবং নির্দেশ পয়েন্টারটি যেখানে প্রয়োজন সেখানে পুনরায় সেট করা হয় এবং ফু যেখান থেকে ছেড়ে যায় সেখান থেকে চলে যেতে থাকে।

অপেক্ষার ধারাবাহিকতা হুবহু একই, অ্যাক্টিভেশনগুলির ক্রমটি স্ট্যাক তৈরি করে না তার স্পষ্ট কারণে রেকর্ডটি গাদাতে রাখা হয় ।

যে প্রতিনিধিটির অপেক্ষার সাথে সাথে টাস্কটির ধারাবাহিকতা দেয় তা হ'ল (1) এমন একটি সংখ্যা যা একটি লুকিং টেবিলের ইনপুট যা নির্দেশ নির্দেশককে দেয় যে আপনাকে পরবর্তী সম্পাদন করতে হবে, এবং (2) স্থানীয় এবং অস্থায়ী সমস্ত মান।

সেখানে কিছু অতিরিক্ত গিয়ার রয়েছে; উদাহরণস্বরূপ, .NET এ ট্রাই ব্লকের মাঝামাঝি শাখা অবৈধ, সুতরাং আপনি কেবলমাত্র একটি চেষ্টা ব্লকের কোডের ঠিকানাটি টেবিলের মধ্যে আটকে রাখতে পারবেন না। তবে এগুলি বুককিপিংয়ের বিশদ। ধারণামূলকভাবে, অ্যাক্টিভেশন রেকর্ডটি সহজেই গাদাতে সরানো হয়।

বর্তমান কল স্ট্যাকের সাথে কী ঘটে, এটি কোনওভাবে সংরক্ষণ করা যায়?

বর্তমান অ্যাক্টিভেশন রেকর্ডে প্রাসঙ্গিক তথ্যটি প্রথমে স্ট্যাকের উপরে রাখা হয় না; এটি পেতে যেতে থেকে গাদা বন্ধ বরাদ্দ করা হয়। (ভাল, আনুষ্ঠানিক প্যারামিটারগুলি স্ট্যাকের উপর বা নিবন্ধগুলিতে সাধারণত পাস করা হয় এবং তারপরে পদ্ধতিটি শুরু হওয়ার পরে একটি গাদা স্থানে অনুলিপি করা হয়))

কলকারীদের অ্যাক্টিভেশন রেকর্ড সংরক্ষণ করা হয় না; প্রত্যাশা সম্ভবত তাদের কাছে ফিরে আসবে, মনে রাখবেন, যাতে তাদের সাথে সাধারণত আচরণ করা হবে।

দ্রষ্টব্য যে এটি অপেক্ষার সরলিকৃত ধারাবাহিকতা পাস করার শৈলীর সাথে এবং আপনি স্কিমের মতো ভাষায় দেখতে পারা সত্য-কল-সাথে-বর্তমান ধারাবাহিক কাঠামোর মধ্যে একটি জার্মান পার্থক্য। সেই ভাষাগুলিতে কলগুলির মধ্যে ধারাবাহিকতা সহ পুরো ধারাবাহিকতা কল-সিসি দ্বারা ক্যাপচার করা হয় ।

কলিং পদ্ধতিটি অপেক্ষা করার আগে যদি অন্য পদ্ধতিতে কল করে - তবে স্ট্যাকটি ওভাররাইট করা হবে না কেন?

এই পদ্ধতিটি কলটি রিটার্ন করে, এবং তাই তাদের সক্রিয়করণের রেকর্ডগুলি আর অপেক্ষা না করার স্থানে থাকে না।

এবং পৃথিবীতে রানটাইম কীভাবে ব্যতিক্রম এবং স্ট্যাকের উদ্বোধনের ক্ষেত্রে এই সমস্ত কিছু দিয়ে কাজ করবে?

কোনও অনিচ্ছাকৃত ব্যতিক্রম ঘটলে, ব্যতিক্রমটি ধরা পড়ে, কার্যের ভিতরে সঞ্চিত থাকে এবং যখন কার্যের ফলাফল আনতে থাকে তখন আবার ফেলে দেওয়া হয়।

আমি আগে উল্লেখ করা সমস্ত বুককিপিং মনে আছে? ব্যতিক্রম শব্দার্থবিজ্ঞান ডান পাওয়া একটি বিশাল ব্যথা ছিল, আমি আপনাকে বলি।

যখন ফলন পৌঁছে যায়, রানটাইম কীভাবে পয়েন্টগুলি ধরে রাখবে যেখানে জিনিসগুলি বাছাই করা উচিত? পুনরুক্তি রাষ্ট্র কীভাবে সংরক্ষণ করা হয়?

একই ভাবে. স্থানীয়দের রাজ্যের স্তূপের দিকে সরানো হয়েছে, এবং নির্দেশের প্রতিনিধিত্বকারী এমন একটি সংখ্যা যা MoveNextপরবর্তী সময় বলা হয়ে গেলে পুনরায় শুরু করা উচিত স্থানীয়দের সাথে সংরক্ষণ করা হয়।

এবং আবারও, ব্যতিক্রমগুলি সঠিকভাবে পরিচালনা করা হয়েছে তা নিশ্চিত করার জন্য একটি পুনরাবৃত্তকারী ব্লকে একগুচ্ছ গিয়ার রয়েছে।


1
প্রশ্ন লেখকের পটভূমির কারণে (এসেম্বারার এট আল), এটি সম্ভবত উল্লেখযোগ্য যে এই দুটি নির্মাণই ম্যানেজড মেমরি ছাড়া সম্ভব নয়। কোনও ম্যানেজড মেমোরি ছাড়াই বন্ধের আজীবন সমন্বয় করার চেষ্টা না করে আপনি আপনার বুটস্ট্র্যাপে ট্রাইপিং করতে পারবেন।
জিম

সমস্ত পৃষ্ঠাগুলির লিঙ্ক পাওয়া যায় নি (404)
ডিজিটাল

আপনার সমস্ত নিবন্ধ এখন অনুপলব্ধ। আপনি তাদের পুনরায় পোস্ট করতে পারেন?
মিশা টার্কজিন

1
@ মাইখাটুরকজিন: তারা এখনও ইন্টারনেটে রয়েছেন; মাইক্রোসফ্ট যেখানে ব্লগ সংরক্ষণাগার রয়েছে সেখানে চলতে থাকে। আমি আস্তে আস্তে এগুলি আমার ব্যক্তিগত সাইটে স্থানান্তরিত করব এবং সময় পেলে এই লিঙ্কগুলি আপডেট করার চেষ্টা করব।
এরিক লিপার্ট

38

yield দুজনের মধ্যে সহজ, তাই এটি পরীক্ষা করা যাক।

বলুন আমাদের আছে:

public IEnumerable<int> CountToTen()
{
  for (int i = 1; i <= 10; ++i)
  {
    yield return i;
  }
}

এটি কিছুটা সংকলিত হয়ে যায় যদি আমরা লিখতাম:

// Deliberately use name that isn't valid C# to not clash with anything
private class <CountToTen> : IEnumerator<int>, IEnumerable<int>
{
    private int _i;
    private int _current;
    private int _state;
    private int _initialThreadId = CurrentManagedThreadId;

    public IEnumerator<CountToTen> GetEnumerator()
    {
        // Use self if never ran and same thread (so safe)
        // otherwise create a new object.
        if (_state != 0 || _initialThreadId != CurrentManagedThreadId)
        {
            return new <CountToTen>();
        }

        _state = 1;
        return this;
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    public int Current => _current;

    object IEnumerator.Current => Current;

    public bool MoveNext()
    {
        switch(_state)
        {
            case 1:
                _i = 1;
                _current = i;
                _state = 2;
                return true;
            case 2:
                ++_i;
                if (_i <= 10)
                {
                    _current = _i;
                    return true;
                }
                break;
        }
        _state = -1;
        return false;
    }

    public void Dispose()
    {
      // if the yield-using method had a `using` it would
      // be translated into something happening here.
    }

    public void Reset()
    {
        throw new NotSupportedException();
    }
}

সুতরাং, হাতে লিখিত প্রয়োগের মতো দক্ষ নয় IEnumerable<int>এবং IEnumerator<int>(উদাহরণস্বরূপ আমরা সম্ভবত পৃথকীকরণ করা নষ্ট করব না _state, _iএবং _currentএই ক্ষেত্রে) তবে খারাপ নয় (নতুন তৈরি করার চেয়ে নিরাপদ থাকা অবস্থায় নিজেকে পুনরায় ব্যবহারের কৌশল) অবজেক্টটি ভাল) এবং অত্যন্ত জটিল- yieldব্যবহার পদ্ধতিগুলির সাথে মোকাবেলা করতে এক্সটেনসিবল ।

এবং অবশ্যই

foreach(var a in b)
{
  DoSomething(a);
}

হিসাবে একই:

using(var en = b.GetEnumerator())
{
  while(en.MoveNext())
  {
     var a = en.Current;
     DoSomething(a);
  }
}

তারপরে উত্পন্ন MoveNext()বারবার বলা হয়।

asyncকেস প্রায় কাছাকাছি একই নীতি, কিন্তু অতিরিক্ত জটিলতা একটি বিট সঙ্গে। অন্য উত্তর কোড থেকে উদাহরণ পুনরায় ব্যবহার করতে যেমন :

private async Task LoopAsync()
{
    int count = 0;
    while(count < 5)
    {
       await SomeNetworkCallAsync();
       count++;
    }
}

কোড উত্পাদন করে:

private struct LoopAsyncStateMachine : IAsyncStateMachine
{
  public int _state;
  public AsyncTaskMethodBuilder _builder;
  public TestAsync _this;
  public int _count;
  private TaskAwaiter _awaiter;
  void IAsyncStateMachine.MoveNext()
  {
    try
    {
      if (_state != 0)
      {
        _count = 0;
        goto afterSetup;
      }
      TaskAwaiter awaiter = _awaiter;
      _awaiter = default(TaskAwaiter);
      _state = -1;
    loopBack:
      awaiter.GetResult();
      awaiter = default(TaskAwaiter);
      _count++;
    afterSetup:
      if (_count < 5)
      {
        awaiter = _this.SomeNetworkCallAsync().GetAwaiter();
        if (!awaiter.IsCompleted)
        {
          _state = 0;
          _awaiter = awaiter;
          _builder.AwaitUnsafeOnCompleted<TaskAwaiter, TestAsync.LoopAsyncStateMachine>(ref awaiter, ref this);
          return;
        }
        goto loopBack;
      }
      _state = -2;
      _builder.SetResult();
    }
    catch (Exception exception)
    {
      _state = -2;
      _builder.SetException(exception);
      return;
    }
  }
  [DebuggerHidden]
  void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0)
  {
    _builder.SetStateMachine(param0);
  }
}

public Task LoopAsync()
{
  LoopAsyncStateMachine stateMachine = new LoopAsyncStateMachine();
  stateMachine._this = this;
  AsyncTaskMethodBuilder builder = AsyncTaskMethodBuilder.Create();
  stateMachine._builder = builder;
  stateMachine._state = -1;
  builder.Start(ref stateMachine);
  return builder.Task;
}

এটি আরও জটিল, তবে একটি খুব অনুরূপ প্রাথমিক নীতি। প্রধান অতিরিক্ত জটিলতা হ'ল এখন GetAwaiter()ব্যবহৃত হচ্ছে। যদি কোনও সময় awaiter.IsCompletedযাচাই করা হয় তবে এটি ফিরে আসে trueকারণ টাস্ক awaitএড ইতিমধ্যে শেষ হয়ে গেছে (উদাহরণস্বরূপ যেখানে এটি সিঙ্ক্রোনালি ফিরে আসতে পারে) তারপরে পদ্ধতিটি রাজ্যগুলির মধ্য দিয়ে চলতে থাকে তবে অন্যথায় এটি ওয়েটারের কলব্যাক হিসাবে নিজেকে সেট করে।

এর সাথে যা ঘটে তা কেবল ওয়েটারের উপর নির্ভর করে, কলব্যাকটি কীভাবে ট্রিগার করে (যেমন async I / O সমাপ্তি, একটি থ্রেড সমাপ্তির সাথে চালিত টাস্ক) এবং নির্দিষ্ট থ্রেডে মার্শালিং করার জন্য বা থ্রেডপুলের থ্রেডে চলার জন্য কী কী প্রয়োজনীয়তা রয়েছে তার উপর নির্ভর করে , আসল কল থেকে কোন প্রসঙ্গের প্রয়োজন হতে পারে এবং নাও হতে পারে ইত্যাদি। যাই হোক না কেন এটি অপেক্ষারত কিছুতে কল MoveNextকরবে এবং এটি হয় পরবর্তী কাজটি (পরবর্তী পর্যন্ত await) দিয়ে চালিয়ে যাবে বা এটি বাস্তবায়িত হচ্ছে এমনটি সম্পন্ন হওয়ার পরে শেষ Taskহয়ে যাবে।


আপনি কি নিজের অনুবাদটি রোল করতে সময় নিয়েছিলেন? ও_ও ইউআওও।
কফডি ডেভলপার

4
@ ডারিওইউ প্রথমটি আমি খুব তাড়াতাড়ি করতে পারি, yieldযখন হাত থেকে রোলড করার প্রচুর অনুবাদ হয়ে গিয়েছে যখন এটি করার কোনও লাভ আছে (সাধারণত একটি অপ্টিমাইজেশন হিসাবে, তবে এটি নিশ্চিত করতে চাই যে সংকলকটি উত্পন্ন সংস্থার নিকটবর্তী হয়) সুতরাং খারাপ অনুমানের মাধ্যমে কিছুই অপ-অনুকূলিত হয়ে ওঠে না। দ্বিতীয়টি প্রথমে অন্য উত্তরে ব্যবহৃত হয়েছিল এবং সেই সময় আমার নিজের জ্ঞানের মধ্যে বেশ কয়েকটি ফাঁক ছিল তাই কোডটি হাতছাড়া করার মাধ্যমে সেই উত্তরটি সরবরাহ করার সময় আমি সেগুলি পূরণ করে নিজেকে উপকৃত করেছি।
জন হান্না

13

ইতিমধ্যে এখানে প্রচুর উত্তরের উত্তর রয়েছে; আমি কেবল কয়েকটি দৃষ্টিভঙ্গি ভাগ করতে যাচ্ছি যা একটি মানসিক মডেল গঠনে সহায়তা করতে পারে।

প্রথমত, একটি asyncপদ্ধতি সংকলক দ্বারা বিভিন্ন টুকরো টুকরো টুকরো করা হয়; awaitএক্সপ্রেশন ফাটল পয়েন্ট। (সহজ পদ্ধতির জন্য ধারণা করা সহজ, লুপগুলি এবং ব্যতিক্রম হ্যান্ডলিং সহ আরও জটিল পদ্ধতিগুলি আরও জটিল স্টেট মেশিনের সংযোজন সহ ভেঙে যায়)।

দ্বিতীয়ত, awaitএকটি মোটামুটি সহজ ক্রম অনুবাদ করা হয়; আমি লুসিয়ার বর্ণনাকে পছন্দ করি , যা কথায় কথায় অনেকটা "যদি প্রতীক্ষাগুলি ইতিমধ্যে সম্পূর্ণ হয়ে থাকে তবে ফলাফলটি পান এবং এই পদ্ধতিটি চালিয়ে যান; অন্যথায়, এই পদ্ধতির অবস্থা সংরক্ষণ করুন এবং ফিরে আসুন"। (আমি আমার asyncপরিচিতিতে খুব অনুরূপ পরিভাষা ব্যবহার করি )।

যখন একটি প্রত্যাশা পৌঁছে যায়, রানটাইম কীভাবে জানতে পারে যে কোডের টুকরোটি পরবর্তী কীভাবে কার্যকর করা উচিত?

পদ্ধতির অবশিষ্টটি সেই প্রত্যাশকের কলব্যাক হিসাবে উপস্থিত রয়েছে (কার্যগুলির ক্ষেত্রে, এই কলব্যাকগুলি ধারাবাহিকতা)। প্রত্যাশিতটি সম্পূর্ণ হয়ে গেলে, এটি তার কলব্যাকগুলি শুরু করে।

মনে রাখবেন যে, কল স্ট্যাক হয় না সংরক্ষণ করা এবং পুনরুদ্ধার; কলব্যাকগুলি সরাসরি ডাকা হয়। ওভারল্যাপড আই / ও এর ক্ষেত্রে, তাদের সরাসরি থ্রেড পুল থেকে ডাকা হবে।

এই কলব্যাকগুলি পদ্ধতিটি সরাসরি চালানো চালিয়ে যেতে পারে, বা তারা অন্য কোথাও চালানোর জন্য সময় নির্ধারণ করতে পারে (উদাহরণস্বরূপ, থ্রেড পুলের awaitউপরের কোনও UI SynchronizationContextএবং I / O ধরা পড়লে )।

এটি কোথায় ছেড়ে যায় যেখানে এটি আবার শুরু করতে পারে তা কীভাবে জানবে এবং কীভাবে এটি মনে আছে?

সব কিছুই কেবল কলব্যাকস। যখন কোনও প্রত্যাশিতটি সম্পূর্ণ হয়, তখন এটি তার কলব্যাকগুলি শুরু করে এবং asyncইতিমধ্যে awaitএটি সম্পাদিত যে কোনও পদ্ধতি আবার শুরু হয়। কলব্যাকটি সেই পদ্ধতির মাঝখানে ঝাঁপিয়ে পড়ে এবং এর স্থানীয় ভেরিয়েবলের সুযোগ।

Callbacks হয় না যে একটি নির্দিষ্ট থ্রেড চালানোর জন্য, এবং তারা না না তাদের callstack পুনরুদ্ধার করেছি।

বর্তমান কল স্ট্যাকের সাথে কী ঘটে, এটি কোনওভাবে সংরক্ষণ করা যায়? কলিং পদ্ধতিটি অপেক্ষা করার আগে যদি অন্য পদ্ধতিতে কল করে - তবে স্ট্যাকটি ওভাররাইট করা হবে না কেন? এবং পৃথিবীতে রানটাইম কীভাবে ব্যতিক্রম এবং স্ট্যাকের উদ্বোধনের ক্ষেত্রে এই সমস্ত কিছু দিয়ে কাজ করবে?

কলস্ট্যাকটি প্রথম স্থানে সংরক্ষণ করা হয়নি; এটি প্রয়োজনীয় নয়।

সিঙ্ক্রোনাস কোডের সাহায্যে আপনি একটি কল স্ট্যাকের সাথে শেষ করতে পারেন যাতে আপনার সমস্ত কলার রয়েছে এবং রানটাইমটি জানে যে এটি ব্যবহার করে কোথায় ফিরতে হবে।

অ্যাসিঙ্ক্রোনাস কোডের সাহায্যে আপনি একগুচ্ছ কলব্যাক পয়েন্টার দিয়ে শেষ করতে পারেন - মূলত কিছু কাজ I / O অপারেশন যা তার কাজ শেষ করে, যা একটি asyncপদ্ধতি পুনরায় শুরু করতে পারে যা তার কাজ শেষ করে, যা কোনও asyncপদ্ধতি পুনরায় শুরু করতে পারে যা তার কাজ শেষ করে, ইত্যাদি etc.

সুতরাং, সিঙ্ক্রোনাস কোড Aকলিং Bকলিংয়ের সাথে Cআপনার কলস্ট্যাকটি দেখতে দেখতে পারা যায়:

A:B:C

যদিও অ্যাসিক্রোনাস কোড কলব্যাক (পয়েন্টার) ব্যবহার করে:

A <- B <- C <- (I/O operation)

যখন ফলন পৌঁছে যায়, রানটাইম কীভাবে পয়েন্টগুলি ধরে রাখবে যেখানে জিনিসগুলি বাছাই করা উচিত? পুনরুক্তি রাষ্ট্র কীভাবে সংরক্ষণ করা হয়?

বর্তমানে, বরং অকার্যকর। :)

এটি অন্য যে কোনও ল্যাম্বদার মতো কাজ করে - পরিবর্তনশীল আজীবন সময় বাড়ানো হয় এবং রেফারেন্সগুলি স্ট্যাকের উপরে বসবাসকারী একটি স্টেট অবজেক্টে স্থাপন করা হয়। সমস্ত গভীর-স্তরের বিশদের জন্য সর্বোত্তম সংস্থান হ'ল জন স্কিটের এডুএসিঙ্ক সিরিজ


7

yieldএবং awaitউভয়ই প্রবাহ নিয়ন্ত্রণ নিয়ে কাজ করার সময় দুটি সম্পূর্ণ ভিন্ন জিনিস। সুতরাং আমি তাদের পৃথকভাবে মোকাবেলা করব।

এর লক্ষ্য yieldহ'ল অলস ক্রমগুলি আরও সহজ করে তোলা। আপনি যখন yieldএটিতে একটি বিবৃতি দিয়ে একটি গণক-লুপ লিখবেন , সংকলকটি আপনাকে দেখতে পাবে না এমন এক টন নতুন কোড তৈরি করে। ফণা অধীনে, এটি আসলে সম্পূর্ণ নতুন বর্গ তৈরি করে। ক্লাসে এমন সদস্য রয়েছে যা লুপের অবস্থা এবং আইকনোমারেবলের একটি বাস্তবায়ন ট্র্যাক করে যাতে প্রতিবার যখন আপনি MoveNextএটিকে লুপটির মাধ্যমে আরও একবার পদক্ষেপে ডাকেন । সুতরাং আপনি যখন এইভাবে একটি ফোরচ লুপ করবেন:

foreach(var item in mything.items()) {
    dosomething(item);
}

উত্পন্ন কোডটি দেখতে এমন কিছু দেখাচ্ছে:

var i = mything.items();
while(i.MoveNext()) {
    dosomething(i.Current);
}

মাইথিং.ইটিমসের বাস্তবায়নের অভ্যন্তরে () হল রাষ্ট্র-মেশিন কোডের একগুচ্ছ যা লুপের একটি "পদক্ষেপ" করবে তারপরে ফিরে আসবে। সুতরাং আপনি যখন এটি উত্সটিতে একটি সাধারণ লুপের মতো লেখেন তখন হুডের নীচে এটি কোনও সরল লুপ নয়। সংকলক কৌতুক। আপনি যদি নিজের দেখতে চান, ILDASM বা ILSpy বা অনুরূপ সরঞ্জামগুলি টানুন এবং উত্পন্ন আইএলটি দেখতে কেমন তা দেখুন। এটি শিক্ষণীয় হতে হবে।

asyncএবং awaitঅন্যদিকে, মাছের সম্পূর্ণ অন্য কেটলি। অ্যাওয়েট হ'ল বিমূর্তে, একটি সিঙ্ক্রোনাইজেশন আদিম। এটি সিস্টেমকে বলার একটি উপায় "এটি শেষ না হওয়া পর্যন্ত আমি চালিয়ে যেতে পারি না।" তবে, যেমন আপনি উল্লেখ করেছেন, সবসময় কোনও থ্রেড জড়িত থাকে না।

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

আপনি যখন বলেন await thisThing(), একটি দুটি জিনিস ঘটে। একটি অ্যাসিঙ্ক পদ্ধতিতে, সংকলকটি প্রকৃতপক্ষে পদ্ধতিটিকে ছোট ছোট খণ্ডে পরিণত করে, প্রতিটি অংশটি "প্রতীক্ষার পূর্বে" বিভাগ এবং "অপেক্ষার পরে" (বা ধারাবাহিকতা) বিভাগে পরিণত হয়। প্রতীক্ষা কার্যকর হলে, টাস্কটি অপেক্ষা করা হচ্ছে, এবং নিম্নলিখিত ধারাবাহিকতা - অন্য কথায়, ফাংশনটির বাকী অংশটি সিঙ্ক্রোনাইজেশন প্রসঙ্গে প্রেরণ করা হবে। প্রসঙ্গটি কাজের সময় নির্ধারণের যত্ন নেয় এবং প্রসঙ্গটি শেষ হয়ে যাওয়ার পরে এটি যে রিটার্ন মান চায় তা পাস করে ধারাবাহিকতা চালায়।

সিঙ্কের প্রসঙ্গটি যতক্ষণ চাইবে তা করতে নির্দ্বিধায় যতক্ষণ না এটি স্টাফিয়ালের সময়সূচি দেয়। এটি থ্রেড পুল ব্যবহার করতে পারে। এটি প্রতিটি কাজ করে একটি থ্রেড তৈরি করতে পারে। এটি তাদের সিঙ্ক্রোনালি চালাতে পারে। বিভিন্ন পরিবেশ (এএসপি.নেট বনাম ডাব্লুপিএফ) বিভিন্ন সিঙ্ক প্রসঙ্গ বাস্তবায়ন সরবরাহ করে যা তাদের পরিবেশের জন্য সর্বোত্তম কিসের ভিত্তিতে বিভিন্ন কাজ করে।

(বোনাস: কখনও কি .ConfigurateAwait(false)ভাবেছে? এটি সিস্টেমকে বলছে যে বর্তমান সিঙ্ক প্রসঙ্গটি ব্যবহার করবেন না (সাধারণত আপনার প্রকল্পের ধরণের ভিত্তিতে - উদাহরণস্বরূপ ডাব্লুপিএফ বনাম এএসপি। নেট) এবং পরিবর্তে থ্রেড পুল ব্যবহার করে এমন ডিফল্ট ব্যবহার করুন)।

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

পিএস ডিফল্ট সিঙ্ক কনটেক্সটগুলির অস্তিত্বের জন্য একটি ব্যতিক্রম রয়েছে - কনসোল অ্যাপ্লিকেশনগুলিতে একটি ডিফল্ট সিঙ্ক প্রসঙ্গ নেই। আরও অনেক তথ্যের জন্য স্টিফেন টুবের ব্লগ পরীক্ষা করুন Check এটি একটি মহান জায়গা তথ্যের জন্য চেহারা এর asyncএবং awaitসাধারণভাবে।


1
"এটি সিস্টেমকে বলছে যে ডিফল্ট সিঙ্ক প্রসঙ্গটি ব্যবহার না করে পরিবর্তে ডিফল্টটি ব্যবহার করুন, যা থ্রেড পুল ব্যবহার করে" আপনি কী বোঝাতে চেয়েছেন এটি পরিষ্কার করতে পারবেন? "ডিফল্ট ব্যবহার করবেন না, ডিফল্ট ব্যবহার করুন"
ক্রোল্টন

3
দুঃখিত, আমার পরিভাষাটি মিশ্রিত করুন, আমি পোস্টটি ঠিক করব। মূলত, আপনি যে পরিবেশে রয়েছেন তার জন্য ডিফল্টটিকে ব্যবহার করবেন না। নেট (যেমন থ্রেড পুল) এর জন্য ডিফল্টটি ব্যবহার করুন।
ক্রিস টাভারেস

খুব সাধারণ, বুঝতে সক্ষম হয়েছিলেন, আপনি আমার ভোট পেয়েছেন :)
এহসান সাজ্জাদ

4

সাধারণত, আমি সিআইএল-এর দিকে তাকাতে চাইব, তবে এগুলির ক্ষেত্রে এটি গোলযোগ।

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

yieldএটি একটি পুরানো এবং সরল বিবৃতি, এবং এটি একটি বেসিক স্টেট মেশিনের জন্য একটি সিনট্যাকটিক চিনি। একটি পদ্ধতিতে ফিরে আসা IEnumerable<T>বা IEnumerator<T>এতে একটি থাকতে পারে yieldযা পরে পদ্ধতিটিকে একটি রাষ্ট্রীয় মেশিন কারখানায় রূপান্তর করে। একটি বিষয় আপনার লক্ষ্য করা উচিত যে কোনও অভ্যন্তর থাকলে আপনি যখন কল করেন তখন এই পদ্ধতির কোনও কোড এই মুহুর্তে চালিত হয় না yield। কারণটি হ'ল আপনি যে কোডটি লিখেছেন তা IEnumerator<T>.MoveNextপদ্ধতিতে অনুলিপি করা হয়েছে , যা এটির মধ্যে থাকা রাষ্ট্রটি পরীক্ষা করে এবং কোডটির সঠিক অংশটি চালায়। yield return x;তারপরে একেবারে কিছুতে রূপান্তরিত হয়this.Current = x; return true;

আপনি যদি কিছু প্রতিফলন করেন তবে আপনি সহজেই নির্মিত রাজ্য মেশিন এবং তার ক্ষেত্রগুলি (কমপক্ষে রাজ্য এবং স্থানীয়দের জন্য) পরিদর্শন করতে পারেন। আপনি ক্ষেত্রগুলি পরিবর্তন করলে আপনি এটি পুনরায় সেট করতেও পারেন।

awaitটাইপ লাইব্রেরি থেকে কিছুটা সমর্থন প্রয়োজন এবং কিছুটা আলাদাভাবে কাজ করে। এটি একটি যুক্তি Taskবা Task<T>যুক্তি লাগে , তারপরে যদি কাজটি সম্পন্ন হয় তবে এর মান হিসাবে ফলাফল দেয় বা এর মাধ্যমে একটি ধারাবাহিকতা নিবন্ধ করে Task.GetAwaiter().OnCompletedasync/ awaitসিস্টেমের সম্পূর্ণ বাস্তবায়নটি ব্যাখ্যা করতে খুব বেশি সময় লাগবে, তবে এটিও রহস্যময় নয়। এটি একটি রাষ্ট্রীয় মেশিনও তৈরি করে এবং এটি অনম্প্লেটেডের ধারাবাহিকতায় পাস করে । যদি টাস্কটি সম্পন্ন হয় তবে এটি তার ফলাফলটি ধারাবাহিকতায় ব্যবহার করে। অপেক্ষার বাস্তবায়ন সিদ্ধান্ত নিয়েছে যে কীভাবে ধারাবাহিকতাটি প্রার্থনা করা যায়। সাধারণত এটি কলিং থ্রেডের সিঙ্ক্রোনাইজেশন প্রসঙ্গটি ব্যবহার করে।

উভয়ই yieldএবং awaitরাষ্ট্রীয় মেশিন গঠনের জন্য তাদের উপস্থিতির উপর ভিত্তি করে পদ্ধতিটিকে আলাদা করতে হবে, মেশিনের প্রতিটি শাখা পদ্ধতির প্রতিটি অংশকে উপস্থাপন করে।

স্ট্যাকস, থ্রেড ইত্যাদির মতো "নিম্ন স্তরের" পদগুলিতে আপনার এই ধারণাগুলি সম্পর্কে ভাবা উচিত নয় These এগুলি বিমূর্ততা, এবং তাদের অভ্যন্তরীণ ক্রিয়াকলাপগুলির জন্য সিএলআর এর কোনও সমর্থন প্রয়োজন হয় না, এটি কেবল সংকলক যা যাদু করে। এটি লুয়ার কর্টিনগুলির থেকে পৃথকভাবে পৃথক , যা রানটাইমের সমর্থন বা সি এর লংজ্যাম্প যা কেবল কালো যাদু।


5
পার্শ্ব নোট : awaitকোনও টাস্ক নিতে হবে না । যা কিছু আছে INotifyCompletion GetAwaiter()তা যথেষ্ট। কীভাবে foreachপ্রয়োজন হয় না IEnumerableতার সাথে সামান্য কিছু, এর সাথে IEnumerator GetEnumerator()যথেষ্ট কিছু ।
IllidanS4 মনিকাকে
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.