HTTPClient.GetAsync (…) প্রত্যাশিত / অ্যাসিঙ্ক ব্যবহার করার সময় কখনই ফিরে আসে না


315

সম্পাদনা: এই প্রশ্নটি দেখে মনে হচ্ছে এটি একই সমস্যা হতে পারে তবে এর কোনও প্রতিক্রিয়া নেই ...

সম্পাদনা: পরীক্ষার ক্ষেত্রে 5 টি কার্যটি আটকে আছে বলে মনে হচ্ছে WaitingForActivation

নেট .৪.৫-এ সিস্টেম. নেট.হট্ট.এইচটিপি.এইচটিপি ক্লায়েন্ট ব্যবহার করে আমি কিছু অদ্ভুত আচরণের মুখোমুখি হয়েছি - যেখানে "অপেক্ষা করা" কল করার ফলাফলের (উদাহরণস্বরূপ) httpClient.GetAsync(...)কখনই ফিরে আসবে না।

এটি কেবলমাত্র কিছু পরিস্থিতিতে দেখা যায় যখন নতুন অ্যাসিঙ্ক / প্রত্যাশিত ভাষা কার্যকারিতা এবং টাস্ক এপিআই ব্যবহার করে - কেবল ধারাবাহিকতা ব্যবহার করার সময় কোডটি সর্বদা কাজ করে বলে মনে হয়।

সমস্যাটি পুনরুত্পাদন করার জন্য এখানে কিছু কোড রয়েছে - নীচের জিইটি শেষ পয়েন্টগুলি প্রকাশ করার জন্য ভিজ্যুয়াল স্টুডিও 11-এ এটি একটি নতুন "এমভিসি 4 ওয়েবএপি প্রকল্প" এ ফেলে দিন:

/api/test1
/api/test2
/api/test3
/api/test4
/api/test5 <--- never completes
/api/test6

এখানের শেষ পয়েন্টগুলির প্রতিটি একই ডেটা (স্ট্যাকওভারফ্লো ডটকম থেকে প্রতিক্রিয়া শিরোনাম) প্রদান করে /api/test5যা ব্যতীত কখনই সম্পূর্ণ হয় না।

আমি কি এইচটিপিপ্লিনেন্ট ক্লাসে একটি বাগের মুখোমুখি হয়েছি, বা আমি কোনওভাবে এপিআইর অপব্যবহার করছি?

পুনরুত্পাদন করার কোড:

public class BaseApiController : ApiController
{
    /// <summary>
    /// Retrieves data using continuations
    /// </summary>
    protected Task<string> Continuations_GetSomeDataAsync()
    {
        var httpClient = new HttpClient();

        var t = httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead);

        return t.ContinueWith(t1 => t1.Result.Content.Headers.ToString());
    }

    /// <summary>
    /// Retrieves data using async/await
    /// </summary>
    protected async Task<string> AsyncAwait_GetSomeDataAsync()
    {
        var httpClient = new HttpClient();

        var result = await httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead);

        return result.Content.Headers.ToString();
    }
}

public class Test1Controller : BaseApiController
{
    /// <summary>
    /// Handles task using Async/Await
    /// </summary>
    public async Task<string> Get()
    {
        var data = await Continuations_GetSomeDataAsync();

        return data;
    }
}

public class Test2Controller : BaseApiController
{
    /// <summary>
    /// Handles task by blocking the thread until the task completes
    /// </summary>
    public string Get()
    {
        var task = Continuations_GetSomeDataAsync();

        var data = task.GetAwaiter().GetResult();

        return data;
    }
}

public class Test3Controller : BaseApiController
{
    /// <summary>
    /// Passes the task back to the controller host
    /// </summary>
    public Task<string> Get()
    {
        return Continuations_GetSomeDataAsync();
    }
}

public class Test4Controller : BaseApiController
{
    /// <summary>
    /// Handles task using Async/Await
    /// </summary>
    public async Task<string> Get()
    {
        var data = await AsyncAwait_GetSomeDataAsync();

        return data;
    }
}

public class Test5Controller : BaseApiController
{
    /// <summary>
    /// Handles task by blocking the thread until the task completes
    /// </summary>
    public string Get()
    {
        var task = AsyncAwait_GetSomeDataAsync();

        var data = task.GetAwaiter().GetResult();

        return data;
    }
}

public class Test6Controller : BaseApiController
{
    /// <summary>
    /// Passes the task back to the controller host
    /// </summary>
    public Task<string> Get()
    {
        return AsyncAwait_GetSomeDataAsync();
    }
}

2
দেখতে - এটি একই সমস্যা বলে মনে করে না, কিন্তু ঠিক নিশ্চিত আপনি এটি সম্পর্কে জানেন করতে, সেখানে বিটা WRT ASYNC পদ্ধতিতে একটি MVC4 বাগ সম্পূর্ণ সিঙ্ক্রোনাস এর stackoverflow.com/questions/9627329/...
জেমস ম্যানিং

ধন্যবাদ - আমি এটির জন্য নজর রাখব। এই ক্ষেত্রে আমি মনে করি যে কলটি করার কারণে পদ্ধতিটি সর্বদা অ্যাসিনক্রোনাস হওয়া উচিত HttpClient.GetAsync(...)?
বেনিয়ামিন ফক্স

উত্তর:


468

আপনি এপিআই এর অপব্যবহার করছেন।

পরিস্থিতি এখানে: এএসপি.এনইটি-তে, একবারে কেবল একটি থ্রেড একটি অনুরোধ পরিচালনা করতে পারে। প্রয়োজনে আপনি কিছু সমান্তরাল প্রক্রিয়াকরণ করতে পারেন (থ্রেড পুল থেকে অতিরিক্ত থ্রেড ধার করা), তবে কেবল একটি থ্রেডের অনুরোধ প্রসঙ্গ থাকবে (অতিরিক্ত থ্রেডের অনুরোধের প্রসঙ্গটি নেই)।

এটি এএসপি.এনইটি পরিচালনা করেSynchronizationContext

ডিফল্টরূপে, যখন আপনি awaitএকটি Task, পদ্ধতিটি কোনও ক্যাপচার SynchronizationContext(বা একটি ক্যাপচার TaskScheduler, যদি না থাকে তবে SynchronizationContext) পুনরায় শুরু হয় । সাধারণত, আপনি যা চান এটি ঠিক এটিই: অ্যাসিনক্রোনাস কন্ট্রোলার অ্যাকশনটি awaitকিছু করতে পারে এবং যখন এটি পুনরায় শুরু হয়, তখন এটি অনুরোধ প্রসঙ্গে পুনরায় শুরু হয়।

সুতরাং, এখানে test5ব্যর্থতা কেন :

  • Test5Controller.Getকার্যকর করে AsyncAwait_GetSomeDataAsync(এএসপি.নেট অনুরোধ প্রসঙ্গে)।
  • AsyncAwait_GetSomeDataAsyncকার্যকর করে HttpClient.GetAsync(এএসপি.নেট অনুরোধ প্রসঙ্গে)।
  • এইচটিটিপি অনুরোধটি প্রেরণ করা হয়েছে, এবং HttpClient.GetAsyncএকটি অপূর্ণিতকে প্রদান করে Task
  • AsyncAwait_GetSomeDataAsyncঅপেক্ষায় Task; যেহেতু এটি সম্পূর্ণ নয়, AsyncAwait_GetSomeDataAsyncএকটি অপূর্ণিতকে ফিরিয়ে দেয় Task
  • Test5Controller.Get এটি Taskশেষ না হওয়া অবধি বর্তমান থ্রেডটিকে অবরুদ্ধ করে ।
  • এইচটিটিপি প্রতিক্রিয়া আসে, এবং Taskফিরে আসা HttpClient.GetAsyncসম্পন্ন হয়।
  • AsyncAwait_GetSomeDataAsyncএএসপি.নেট অনুরোধ প্রসঙ্গে পুনরায় শুরু করার চেষ্টা করে। তবে ইতিমধ্যে সেই প্রসঙ্গে একটি থ্রেড রয়েছে: থ্রেডটি ব্লকড Test5Controller.Get
  • অচলাবস্থা।

অন্যরা কেন কাজ করে তা এখানে:

  • ( test1, test2এবং এবং test3): Continuations_GetSomeDataAsyncথ্রেড পুলের ধারাবাহিকতাটি এএসপি.নেট অনুরোধ প্রসঙ্গে বাইরে শিডিয়ুল করে। এটি অনুরোধ প্রসঙ্গে পুনরায় প্রবেশ না করেই Taskফিরে আসাটিকে Continuations_GetSomeDataAsyncসম্পূর্ণ করতে দেয় ।
  • ( test4এবং test6): যেহেতু Taskহয় প্রতীক্ষিত , ASP.NET অনুরোধ থ্রেড অবরুদ্ধ করা হয় না। এটি AsyncAwait_GetSomeDataAsyncচালিয়ে যাওয়ার জন্য প্রস্তুত হলে এটি ASP.NET অনুরোধ প্রসঙ্গটি ব্যবহার করতে দেয় ।

এবং এখানে সেরা অভ্যাসগুলি:

  1. আপনার "গ্রন্থাগার" asyncপদ্ধতিতে, ConfigureAwait(false)যখনই সম্ভব ব্যবহার করুন । আপনার ক্ষেত্রে, এটি হতে AsyncAwait_GetSomeDataAsyncহবেvar result = await httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
  2. Taskএস এ ব্লক করবেন না ; এটা asyncসব নিচে। অন্য কথায়, এর awaitপরিবর্তে GetResult( Task.Resultএবং এর Task.Waitসাথে প্রতিস্থাপন করা উচিত await) ব্যবহার করুন।

এইভাবে, আপনি উভয় সুবিধা পান: ধারাবাহিকতা ( AsyncAwait_GetSomeDataAsyncপদ্ধতিটির বাকী অংশ ) একটি বেসিক থ্রেড পুল থ্রেডে চালিত হয় যা এএসপি.নেট অনুরোধ প্রসঙ্গে প্রবেশ করতে হবে না; এবং নিয়ামক নিজেই async(যা কোনও অনুরোধের থ্রেডকে ব্লক করে না)।

অধিক তথ্য:

আপডেট 2012-07-13: এই উত্তরটি একটি ব্লগ পোস্টে অন্তর্ভুক্ত করা হয়েছে


2
এএসপি.এনইটি-র জন্য এমন কিছু ডকুমেন্টেশন রয়েছে SynchroniztaionContextযা ব্যাখ্যা করে যে কিছু অনুরোধের প্রসঙ্গে কেবল একটি থ্রেড থাকতে পারে? যদি না হয়, আমার মনে হয় সেখানে থাকা উচিত।
সোভিক

8
এটি এএফআইকে কোথাও নথিভুক্ত নয়।
স্টিফেন ক্লিয়ারি

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

3
এমন কোন পরিস্থিতি রয়েছে যেখানে একটি এসপি নেট প্রসঙ্গে কনফিগারিউইট (মিথ্যা) ব্যবহারের পরামর্শ দেওয়া হচ্ছে না? এটি আমার কাছে মনে হয় যে এটি সর্বদা ব্যবহার করা উচিত এবং এটি কেবলমাত্র কোনও ইউআই প্রসঙ্গে এটি ব্যবহার করা উচিত নয় যেহেতু আপনাকে ইউআইতে সিঙ্ক করতে হবে। নাকি আমি কথাটি মিস করছি?
অ্যালেক্সগাদ

3
এএসপি.এনইটি SynchronizationContextকিছু গুরুত্বপূর্ণ কার্যকারিতা সরবরাহ করে: এটি অনুরোধের প্রসঙ্গে প্রবাহিত করে। এতে কুকি থেকে সংস্কৃতিতে প্রমাণীকরণ থেকে শুরু করে সব ধরণের স্টাফ রয়েছে। সুতরাং এএসপি.নেটে, ইউআইতে ফিরে সিঙ্ক করার পরিবর্তে আপনি অনুরোধ প্রসঙ্গে সিঙ্ক করুন। এই খুব শীঘ্রই পরিবর্তন হতে পারে: নতুন ApiControllerএকটি আছে HttpRequestMessageএকটি সম্পত্তি হিসেবে প্রসঙ্গ - তাই এটা হতে পারে মাধ্যমে প্রসঙ্গ প্রবাহিত করা প্রয়োজন হবে না SynchronizationContext- কিন্তু আমি এখনো জানি না।
স্টিফেন ক্লিয়ারি 10:51

61

সম্পাদনা করুন: সাধারণত অচলাবস্থা এড়ানোর জন্য সর্বশেষ খাদ্যের চেষ্টা ব্যতীত নীচে না করার চেষ্টা করুন। স্টিফেন ক্লিয়ারি থেকে প্রথম মন্তব্য পড়ুন।

এখান থেকে দ্রুত ঠিক করুন । লেখার পরিবর্তে:

Task tsk = AsyncOperation();
tsk.Wait();

চেষ্টা করুন:

Task.Run(() => AsyncOperation()).Wait();

অথবা যদি আপনার কোনও ফলাফলের প্রয়োজন হয়:

var result = Task.Run(() => AsyncOperation()).Result;

উত্স থেকে (উপরের উদাহরণটি মেলাতে সম্পাদিত):

AsyncOperation এখন থ্রেডপুল-এ আহ্বান করা হবে, যেখানে কোনও সিঙ্ক্রোনাইজেশন কনটেক্সট হবে না এবং অ্যাসিঙ্কঅপেশের অভ্যন্তরে ব্যবহৃত ধারাবাহিকতাগুলি পুনরায় চলমান থ্রেডে বাধ্য করা হবে না।

আমার কাছে এটি একটি ব্যবহারযোগ্য বিকল্প হিসাবে দেখায় যেহেতু আমার কাছে এটিকে সমস্ত উপায়ে অ্যাসিঙ্ক করার বিকল্প নেই (যা আমি পছন্দ করব)।

উত্স থেকে:

নিশ্চিত হয়ে নিন যে FooAsync পদ্ধতিতে অপেক্ষারত মার্শালের কোনও প্রসঙ্গে ফিরে পাওয়া যায় না। এটি করার সহজতম উপায় হ'ল থ্রেডপুল থেকে অ্যাসিনক্রোনাস কাজ শুরু করা, যেমন কোনও টাস্ক.রুনে অনুরোধটি মোড়ানো দ্বারা eg

int সিঙ্ক () Tas ফেরত টাস্ক.আর (() => লাইব্রেরি.ফুএসিঙ্ক ()) ফলাফল; }

FooAsync এখন থ্রেডপুলটিতে ডাকা হবে, যেখানে কোনও সিঙ্ক্রোনাইজেশন কনটেক্সট হবে না এবং FooAncnc এর অভ্যন্তরে ব্যবহৃত ধারাবাহিকতাগুলি সিঙ্ককে আহ্বান করা থ্রেডে ফিরে আসতে বাধ্য করা হবে ()।


7
আপনার উত্স লিঙ্কটি পুনরায় পড়তে চায়; লেখক এটি না করার পরামর্শ দিয়েছেন । এটা কি কাজ করে? হ্যাঁ, তবে কেবল সেই অর্থে যে আপনি অচলাবস্থা এড়াতে পারেন। এই সমাধানটি এএসপি.এনইটি কোডের সমস্ত সুবিধা asyncউপেক্ষা করে এবং বাস্তবে স্কেল এ সমস্যা তৈরি করতে পারে। বিটিডাব্লু, ConfigureAwaitকোনও দৃশ্যে "যথাযথ অ্যাসিঙ্ক আচরণ" ভঙ্গ করে না; এটি আপনার লাইব্রেরি কোডে ব্যবহার করা উচিত ঠিক।
স্টিফেন ক্লিয়ারি

2
এটি পুরো প্রথম বিভাগ, শিরোনামে শিরোনাম Avoid Exposing Synchronous Wrappers for Asynchronous Implementations। পোস্টের সমগ্র বাকি এটা করতে কয়েক বিভিন্ন উপায়ে ব্যাখ্যা করা হয় যদি আপনি একেবারে প্রয়োজন হয়।
স্টিফেন ক্লিয়ারি

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

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

1
অ্যাসিঙ্ককে সিঙ্কে রূপান্তর করতে আমি একটি এক্সটেনশন পদ্ধতি তৈরি করে শেষ করেছি। আমি এখানে কোথাও তার একইভাবে পড়েছি। নেট ফ্রেমওয়ার্ক এটি করে: সর্বজনীন স্ট্যাটিক ট্র্যাসল্ট রানসিংক <TResult> (এই ফানক <টাস্ক <TResult>> ফানক) {রিটার্ন _ টাস্কফ্যাক্টরি art স্টার্টনেউ (ফানক) nউনআর্যাপ () .গেটওয়াটার () .GetResult (); }
সামনারিক

10

আপনি ব্যবহার করছেন যেহেতু .Resultবা .Waitবা awaitএই একটি ঘটাচ্ছে শেষ হবে অচলাবস্থা আপনার কোডে।

অচলাবস্থা রোধের জন্য ConfigureAwait(false)আপনি asyncপদ্ধতিগুলিতে ব্যবহার করতে পারেন

এটার মত:

var result = await httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead)
                             .ConfigureAwait(false);

ConfigureAwait(false)অ্যাসিঙ্ক কোডটি ব্লক করবেন না এর জন্য আপনি যেখানেই সম্ভব ব্যবহার করতে পারেন ।


2

এই দুটি স্কুল সত্যই বাদ দিচ্ছে না।

এখানে দৃশ্যধারণ করা হয়েছে যেখানে আপনাকে কেবল ব্যবহার করতে হবে

   Task.Run(() => AsyncOperation()).Wait(); 

বা মত কিছু

   AsyncContext.Run(AsyncOperation);

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

আমার যে লাইব্রেরিটি দরকার তা হ'ল অ্যাসিঙ্ক হিসাবে এটি অসিঙ্ক চালিত হবে expected

একমাত্র বিকল্প। এটি একটি সাধারণ সিঙ্ক কল হিসাবে চালান।

আমি একে অপরের নিজস্ব কথা বলছি।


সুতরাং আপনি আপনার উত্তরের প্রথম বিকল্পটি পরামর্শ দিচ্ছেন?
ডন

1

আমি ওপিতে সরাসরি প্রাসঙ্গিকতার চেয়ে সম্পূর্ণতার জন্য এটি এখানে রাখছি। একটি HttpClientঅনুরোধ ডিবাগ করতে আমি প্রায় এক দিন কাটিয়েছি , ভেবে অবাক হলাম কেন আমি কখনই কোনও সাড়া পাই না।

অবশেষে পাওয়া গেছে যে আমি ভুলে গিয়েছিলেন কল স্ট্যাক নিচে আরও কল।awaitasync

একটি সেমিকোলন অনুপস্থিত হিসাবে ভাল হিসাবে অনুভূত।


-1

আমি এখানে খুঁজছি:

http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.taskawaiter(v=vs.110).aspx

এবং এখানে:

http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.taskawaiter.getresult(v=vs.110).aspx

এবং দেখছি:

এই ধরণের এবং এর সদস্যগণ সংকলক দ্বারা ব্যবহারের উদ্দেশ্যে।

awaitসংস্করণটি কাজ করে, এবং কাজ করার 'সঠিক' উপায় বিবেচনা করে কি আপনার এই প্রশ্নের উত্তরটির সত্যই দরকার?

আমার ভোটটি হ'ল: API টির অপব্যবহার


আমি এটি লক্ষ্য করি নি, যদিও আমি অন্যান্য চারপাশে অন্যান্য ভাষা দেখেছি যা ইঙ্গিত করে যে GetResult () API ব্যবহার করা একটি সমর্থিত (এবং প্রত্যাশিত) ব্যবহার-কেস।
বেনিয়ামিন ফক্স

1
Test5Controller.Get()তারপরেও , আপনি যদি নিম্নলিখিতটি দিয়ে ওয়েটারটি অপসারণের জন্য চুল্লি করেন: var task = AsyncAwait_GetSomeDataAsync(); return task.Result;একই আচরণ লক্ষ্য করা যায়।
বেনিয়ামিন ফক্স
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.