একটি অ্যাসিঙ্ক / অপেক্ষার উদাহরণ যা অচলাবস্থার কারণ হয়ে দাঁড়ায়


97

সি # 's async/ ব্যবহার করে অ্যাসিক্রোনাস প্রোগ্রামিংয়ের জন্য আমি কয়েকটি সেরা অনুশীলন পেয়েছিawait কীওয়ার্ডগুলি (আমি সি # 5.0 এ নতুন) আমি পেয়েছি।

প্রদত্ত পরামর্শগুলির মধ্যে একটি ছিল:

স্থায়িত্ব: আপনার সিঙ্ক্রোনাইজেশন প্রসঙ্গে জানুন

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

public ActionResult ActionAsync()
{
    // DEADLOCK: this blocks on the async task
    var data = GetDataAsync().Result;

    return View(data);
}

private async Task<string> GetDataAsync()
{
    // a very simple async method
    var result = await MyWebService.GetDataAsync();
    return result.ToString();
}

যদি আমি নিজে এটি ছড়িয়ে দেওয়ার চেষ্টা করি, মূল থ্রেডটি একটি নতুনকে প্রসারিত করে MyWebService.GetDataAsync();, তবে যেহেতু মূল থ্রেডটি সেখানে অপেক্ষা করছে, এটি ফলাফলটির জন্য অপেক্ষা করে GetDataAsync().Result। এদিকে, ডেটা প্রস্তুত আছে বলে। মূল থ্রেড কেন চালিয়ে যায় না এটির ধারাবাহিকতা যুক্তিযুক্ত এবং এর থেকে স্ট্রিং ফলাফল প্রদান করেGetDataAsync() ?

কেউ দয়া করে আমাকে ব্যাখ্যা করতে পারেন কেন উপরের উদাহরণটিতে অচলাবস্থা রয়েছে? সমস্যাটি কী তা সম্পর্কে আমি সম্পূর্ণ নির্বোধ ...


আপনি কি সত্যিই নিশ্চিত যে গেটডেটাএসেঙ্ক এটি শেষ করে দিয়েছে? বা এটি আটকে পড়ে কেবল লক করে এবং অচলাবস্থার সৃষ্টি করে না?
অ্যান্ড্রে

এটি দেওয়া হয়েছিল যে উদাহরণ। আমার বোঝার জন্য এটি স্টাফ শেষ করা উচিত এবং কিছুটা প্রস্তুত ফলস্বরূপ ফলাফল হওয়া উচিত ...
ডরার ওয়েইস

4
আপনি এমনকি কাজের জন্য অপেক্ষা করছেন কেন? পরিবর্তে আপনার অপেক্ষা করা উচিত কারণ আপনি মূলত অ্যাসিঙ্ক মডেলটির সমস্ত সুবিধা হারিয়েছেন।
টনি পেট্রিনা

To add to @ToniPetrina's point, even w/o the deadlock problem, var data = GetDataAsync().Result; is a line of code that should never be done in a context that you should not block (UI or ASP.NET request). Even if it doesn't deadlock, it is blocking the thread an indeterminate amount of time. So basically its a terrible example. [You need to get off of the UI thread before executing code like that, or use await there also, as Toni suggests.]
ToolmakerSteve

উত্তর:


82

Take a look at this example, Stephen has a clear answer for you:

সুতরাং শীর্ষ স্তরের পদ্ধতি ( Button1_Clickইউআই / MyController.Getএএসপি.এনইটির জন্য) দিয়ে শুরু করে এটিই ঘটে :

  1. শীর্ষ স্তরের পদ্ধতি কলগুলি GetJsonAsync(UI / ASP.NET প্রসঙ্গে)।

  2. GetJsonAsyncREST অনুরোধ কল করে HttpClient.GetStringAsync(এখনও প্রসঙ্গে রয়েছে) শুরু করে।

  3. GetStringAsyncএকটি অপূর্ণবিশেষ প্রদান করে Task, নির্দেশ করে যে অনুরোধটি সম্পূর্ণ হয়নি request

  4. GetJsonAsyncTaskদ্বারা ফিরে প্রতীক্ষিত GetStringAsync। প্রসঙ্গটি ক্যাপচার করা হয়েছে এবং GetJsonAsyncপরে পদ্ধতিটি চালিয়ে যেতে ব্যবহার করা হবে । পদ্ধতিটি সম্পূর্ণ নয় বলে ইঙ্গিত করে GetJsonAsyncএকটি অপ্রাপ্তকে ফেরত Taskদেয় GetJsonAsync

  5. শীর্ষ-স্তরের পদ্ধতিটি সিঙ্ক্রোনালিভাবে Taskফিরে আসাগুলিতে ব্লক করে GetJsonAsync। এটি প্রসঙ্গ থ্রেডকে ব্লক করে।

  6. ... অবশেষে, REST অনুরোধটি সম্পূর্ণ হবে। এটি Taskযে দ্বারা ফিরে এসেছিল তা সম্পূর্ণ করে GetStringAsync

  7. এর ধারাবাহিকতা GetJsonAsyncএখন চালানোর জন্য প্রস্তুত এবং প্রসঙ্গটি উপলব্ধ হওয়ার জন্য এটি অপেক্ষা করে যাতে এটি প্রসঙ্গে কার্যকর করা যায়।

  8. অচলাবস্থা । শীর্ষ স্তরের পদ্ধতিটি প্রসঙ্গের থ্রেডটি ব্লক করছে GetJsonAsync, সম্পূর্ণ হওয়ার GetJsonAsyncজন্য অপেক্ষা করছে এবং প্রসঙ্গটি মুক্ত হওয়ার জন্য অপেক্ষা করছে যাতে এটি সম্পূর্ণ হয় can ইউআই উদাহরণস্বরূপ, "প্রসঙ্গ" হ'ল ইউআই প্রসঙ্গ; এএসপি.নেট উদাহরণের জন্য, "প্রসঙ্গ" হ'ল এএসপি.নেট অনুরোধ প্রসঙ্গ। এই জাতীয় ডেডলকটি "প্রসঙ্গ" এর জন্যই হতে পারে।

আপনার আর একটি লিঙ্কটি পড়তে হবে: ওয়েইট এবং ইউআই, এবং ডেডলকগুলি! আহারে!


23
  • ফ্যাক্ট 1: টিস্যুটি সম্পূর্ণ হয়ে GetDataAsync().Result;গেলে ফিরে আসবে GetDataAsync(), ইতিমধ্যে এটি ইউআই থ্রেডকে ব্লক করে
  • ঘটনা 2: প্রতীক্ষার ধারাবাহিকতাটি return result.ToString()কার্যকর করার জন্য ইউআই থ্রেডে সারিযুক্ত
  • ঘটনা 3: GetDataAsync()এর ক্রমযুক্ত ধারাবাহিকতা চালিত হলে ফিরে আসা কাজটি সম্পূর্ণ হবে
  • ঘটনা 4: সারিবদ্ধ ধারাবাহিকতা কখনই চালিত হয় না, কারণ ইউআই থ্রেডটি অবরুদ্ধ করা হয়েছে (ঘটনা 1)

অচল!

ফ্যাক্ট 1 বা ফ্যাক্ট 2 এড়ানোর জন্য প্রদত্ত বিকল্পগুলির মাধ্যমে অচলাবস্থা ভেঙে দেওয়া যেতে পারে।

  • 1,4 এড়ান। ইউআই থ্রেডটি ব্লক করার পরিবর্তে, ব্যবহার করুন var data = await GetDataAsync(), যা ইউআই থ্রেডটি চলমান রাখতে দেয়
  • 2,3 এড়ান। অবরুদ্ধ নয় এমন একটি আলাদা থ্রেডের প্রতীক্ষার ধারাবাহিকতাটি সারিবদ্ধ করুন, যেমন ব্যবহার var data = Task.Run(GetDataAsync).Result, যা থ্রেডপুল থ্রেডের সিঙ্ক প্রসঙ্গে ধারাবাহিকতা পোস্ট করবে। এটি টাস্কটি GetDataAsync()সম্পূর্ণ করার অনুমতি দেয় ।

This is explained really well in an article by Stephen Toub, about half way down where he uses the example of DelayAsync().


সম্পর্কে, var data = Task.Run(GetDataAsync).Resultএটি আমার কাছে নতুন। আমি সর্বদা ভেবেছিলাম .Resultপ্রথম প্রতীক্ষার সাথে GetDataAsyncসাথে আঘাতের সাথে সাথে বাইরেরটি সহজেই উপলব্ধ dataহবে , তাই সর্বদা থাকবে default। মজাদার.
নওফাল

19

আমি কেবল একটি এএসপি.এনইটি এমভিসি প্রকল্পে এই সমস্যাটি নিয়ে ফিরছিলাম। আপনি যখন asyncএকটি থেকে পদ্ধতিগুলি কল করতে চান PartialView, আপনাকে এটি তৈরি করার অনুমতি দেওয়া হয় না PartialView async। আপনি যদি একটি ব্যতিক্রম পাবেন।

আপনি যে asyncসিঙ্ক পদ্ধতিতে কোনও পদ্ধতিতে কল করতে চান সেই দৃশ্যে আপনি নীচের সাধারণ কার্যকারিতাটি ব্যবহার করতে পারেন :

  1. কল করার আগে, সাফ করুন SynchronizationContext
  2. কল করুন, এখানে আর কোনও অচলাবস্থা থাকবে না, এটি শেষ হওয়ার জন্য অপেক্ষা করুন
  3. পুনরুদ্ধার SynchronizationContext

উদাহরণ:

public ActionResult DisplayUserInfo(string userName)
{
    // trick to prevent deadlocks of calling async method 
    // and waiting for on a sync UI thread.
    var syncContext = SynchronizationContext.Current;
    SynchronizationContext.SetSynchronizationContext(null);

    //  this is the async call, wait for the result (!)
    var model = _asyncService.GetUserInfo(Username).Result;

    // restore the context
    SynchronizationContext.SetSynchronizationContext(syncContext);

    return PartialView("_UserInfo", model);
}

3

Another main point is that you should not block on Tasks, and use async all the way down to prevent deadlocks. Then it will be all asynchronous not synchronous blocking.

public async Task<ActionResult> ActionAsync()
{

    var data = await GetDataAsync();

    return View(data);
}

private async Task<string> GetDataAsync()
{
    // a very simple async method
    var result = await MyWebService.GetDataAsync();
    return result.ToString();
}

6
What if I want the main (UI) thread to be blocked until the task finishes? Or in a Console app for example? Let's say I want to use HttpClient, which only supports async... How do I use it synchronously without the risk of deadlock? This must be possible. If WebClient can be used that way (because of having sync methods) and works perfectly, then why couldn't it be done with HttpClient too?
Dexter

উপরে ফিলিপ নাগানের উত্তর দেখুন (আমি জানি যে এই মন্তব্যের পরে এটি পোস্ট করা হয়েছিল): অপেক্ষার ধারাবাহিকতাটি কোনও অন্য থ্রেড যা অবরুদ্ধ নয়, যেমন সারিবদ্ধ করুন, যেমন var ডেটা = টাস্ক.রুন (গেটডাটাঅ্যাসেন্স) ব্যবহার করুন es ফলাফল
জেরোইন

@ ডেক্সটার - পুনরায় " যদি আমি কাজটি শেষ না হওয়া পর্যন্ত মূল (ইউআই) থ্রেডটি ব্লক করতে চাই তবে কী হবে? " - আপনি কি সত্যিই ইউআই থ্রেডটি অবরুদ্ধ করতে চান, যার অর্থ ব্যবহারকারী কিছুই করতে পারে না, এমনকি বাতিল করতে পারে না - বা হয় আপনি যে পদ্ধতিতে রয়েছেন তা চালিয়ে যেতে চান না? "অপেক্ষারত" বা "টাস্ক। কনটিনিউ" এর সাথে উত্তরোত্তর কেসটি পরিচালনা করে।
টুলমেকারস্টেভ

@ টুলমেকারস্টেভ অবশ্যই আমি পদ্ধতিটি চালিয়ে যেতে চাই না। কিন্তু আমি কেবল না পারেন অপেক্ষায় রয়েছেন ব্যবহার কারণ আমি সব ASYNC উপায় পারেন ব্যবহার করতে পারবেন না - HttpClient আবাহন করা হয় মূল , অবশ্যই যা ASYNC হতে পারে না। এবং তারপর আমি একটি কনসোল অ্যাপ্লিকেশানে সব এই কাজ উল্লিখিত - এই ক্ষেত্রে আমি ঠিক সাবেক চাই - এমনকি আমি না আমার অ্যাপ্লিকেশানের চান হতে বহু-থ্রেডেড। সবকিছু ব্লক করুন ।
ডেক্সটার

-1

আমার কাছে আসা একটি কাজ হল Joinফলাফল জিজ্ঞাসা করার আগে কার্যটিতে একটি এক্সটেনশন পদ্ধতি ব্যবহার করা ।

কোড চেহারাটি এর মতো:

public ActionResult ActionAsync()
{
  var task = GetDataAsync();
  task.Join();
  var data = task.Result;

  return View(data);
}

যোগদানের পদ্ধতিটি হ'ল:

public static class TaskExtensions
{
    public static void Join(this Task task)
    {
        var currentDispatcher = Dispatcher.CurrentDispatcher;
        while (!task.IsCompleted)
        {
            // Make the dispatcher allow this thread to work on other things
            currentDispatcher.Invoke(delegate { }, DispatcherPriority.SystemIdle);
        }
    }
}

আমি এই সমাধানের অসুবিধাগুলি দেখতে ডোমেনে যথেষ্ট নেই (যদি থাকে)

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