সি # তে সিঙ্ক্রোনাস পদ্ধতি থেকে অ্যাসিক্রোনাস পদ্ধতিটি কীভাবে বলা যায়?


861

আমার একটি public async void Foo()পদ্ধতি রয়েছে যা আমি সিঙ্ক্রোনাস পদ্ধতি থেকে কল করতে চাই। এখনও অবধি আমি এমএসডিএন ডকুমেন্টেশন থেকে যা দেখেছি সবগুলিই অ্যাসিঙ্ক পদ্ধতির মাধ্যমে অ্যাসিঙ্ক পদ্ধতিগুলি কল করছে, তবে আমার পুরো প্রোগ্রামটি অ্যাসিঙ্ক পদ্ধতিতে নির্মিত হয়নি।

এটা কি সম্ভব?

এই পদ্ধতিগুলিকে একটি অ্যাসিনক্রোনাস পদ্ধতি থেকে কল করার একটি উদাহরণ এখানে দেওয়া হয়েছে: http://msdn.microsoft.com/en-us/library/hh300224(v=vs.110).aspx

এখন আমি এই সিঙ্ক পদ্ধতিগুলি থেকে এ্যাসিঙ্ক পদ্ধতিগুলি কল করার বিষয়ে সন্ধান করছি।


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

1
কারণ আপনার async void Foo()পদ্ধতিটি কোনও ফেরত দেয় না এর Taskঅর্থ হ'ল একজন কলকারী কখন এটি শেষ করবে তা জানতে পারে না, তার Taskপরিবর্তে অবশ্যই ফিরে আসতে হবে।
দাই

1
কোনও ইউআই থ্রেডে এটি কীভাবে করা যায় সম্পর্কিত সম্পর্কিত Q / a এর সাথে সংযুক্ত
নসরটিটিও

উত্তর:


710

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

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

সমাধান এ

আপনার যদি একটি সাধারণ অ্যাসিনক্রোনাস পদ্ধতি থাকে যা এর প্রসঙ্গে পুনরায় সিঙ্ক্রোনাইজ করার প্রয়োজন হয় না, তবে আপনি ব্যবহার করতে পারেন Task.WaitAndUnwrapException:

var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();

আপনি না ব্যবহার চানTask.Wait বা Task.Resultকারণ তারা ব্যতিক্রমগুলি মুড়ে ফেলে AggregateException

এই সমাধানটি কেবল তখনই উপযুক্ত যদি MyAsyncMethodএর প্রসঙ্গে সিঙ্ক্রোনাইজ না করে। অন্য কথায়, প্রতিটিawait ইন এর MyAsyncMethodসাথে শেষ হওয়া উচিত ConfigureAwait(false)। এর অর্থ এটি কোনও UI উপাদান আপডেট করতে বা ASP.NET অনুরোধ প্রসঙ্গে অ্যাক্সেস করতে পারে না।

সমাধান বি

যদি MyAsyncMethodএর প্রসঙ্গে আবার সিঙ্ক্রোনাইজ করার দরকার হয় তবে আপনি AsyncContext.RunTaskনেস্টেড প্রসঙ্গটি সরবরাহ করতে ব্যবহার করতে সক্ষম হতে পারেন :

var result = AsyncContext.RunTask(MyAsyncMethod).Result;

* আপডেট 4/14/2014: গ্রন্থাগারের আরও সাম্প্রতিক সংস্করণগুলিতে এপিআই নীচে রয়েছে:

var result = AsyncContext.Run(MyAsyncMethod);

(এটি ব্যবহার করা ঠিক আছে Task.Result এই উদাহরণে কারণ ব্যতিক্রমগুলি RunTaskপ্রচার করবে Task)।

AsyncContext.RunTaskপরিবর্তে আপনার প্রয়োজন হতে পারেTask.WaitAndUnwrapException হ'ল উইনফোর্ডস / ডাব্লুপিএফ / এসএল / এএসপি.নেটে ঘটে যাওয়া বরং একটি সূক্ষ্ম অচলাবস্থা সম্ভাবনা:

  1. একটি সিঙ্ক্রোনাস পদ্ধতি একটি এসিঙ্ক পদ্ধতিকে কল করে, এটি প্রাপ্ত করে Task
  2. সিঙ্ক্রোনাস পদ্ধতিটি একটি অবরুদ্ধ অপেক্ষা করে Task
  3. asyncপদ্ধতি ব্যবহারawait ছাড়া ConfigureAwait
  4. Taskএই অবস্থায় সম্পন্ন করতে পারবেন না কারণ এটি শুধুমাত্র সমাপ্ত যখন asyncপদ্ধতি সমাপ্ত হয়; asyncপদ্ধতি সম্পন্ন করতে পারবে না এটা তার ধারাবাহিকতা নির্দিষ্ট সময় নির্ধারণের জন্য চেষ্টা করছে কারণSynchronizationContext , এবং WinForms / WPF / SL বিভাগ: / ASP.NET ধারাবাহিকতা কারণ সমকালীন পদ্ধতি ইতিমধ্যে যে প্রেক্ষাপটে চলমান চালানোর জন্য অনুমতি দেয় না।

এটি প্রতিটি কারণেই যতটা সম্ভব ConfigureAwait(false)প্রতিটি asyncপদ্ধতির মধ্যে ব্যবহার করা ভাল ধারণা ।

সমাধান সি

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

var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();

তবে এই সমাধানটির জন্য MyAsyncMethodথ্রেড পুলের প্রসঙ্গে কাজ করা দরকার will সুতরাং এটি ইউআই উপাদানগুলি আপডেট করতে বা এএসপি.নেট অনুরোধ প্রসঙ্গে অ্যাক্সেস করতে পারে না। এবং যে ক্ষেত্রে, ভাল হিসাবে আপনি যোগ করতে পারেন ConfigureAwait(false)তারawait বিবৃতিতে পারেন এবং সমাধান এ ব্যবহার করতে পারেন

আপডেট, 2019-05-01: বর্তমান "সর্বনিম্ন-সবচেয়ে খারাপ অভ্যাস" এখানে একটি এমএসডিএন নিবন্ধে রয়েছে


9
সমাধান এটিকে আমি যা চাই তা মনে হয় তবে এটি টাস্কের মতো লাগে W এটিতে কেবল টাস্ক রয়েছে a নতুন সংস্করণ দিয়ে এটি কীভাবে করবেন কোনও ধারণা? বা এটি কি আপনার লেখা কাস্টম এক্সটেনশন পদ্ধতি?
প্রাণঘাতী

3
WaitAndUnwrapExceptionআমার অ্যাসিএনএক্স লাইব্রেরি থেকে আমার নিজস্ব পদ্ধতি । অফিসিয়াল .NET libs সিঙ্ক এবং অ্যাসিঙ্ক কোড মিক্স করার জন্য খুব বেশি সহায়তা দেয় না (এবং সাধারণভাবে, আপনার এটি করা উচিত নয়!)। আমি .NET 4.5 আরটিডাব্লু এবং একটি নতুন নন-এক্সপি ল্যাপটপের অপেক্ষায় রয়েছি এসিএনেক্সেক্স আপডেট করার আগে 4.5 তে চালানো (বর্তমানে আমি 4.5 এর জন্য বিকাশ করতে পারি না কারণ আমি আরও কয়েক সপ্তাহ এক্সপিতে আটকে আছি)।
স্টিফেন ক্লিয়ারি

12
AsyncContextএখন একটি Runপদ্ধতিতে ল্যাম্বডা এক্সপ্রেশন লাগে, তাই আপনার ব্যবহার করা উচিতvar result = AsyncContext.Run(() => MyAsyncMethod());
স্টিফেন ক্লিয়ারি

1
আমি আপনার লাইব্রেরিটি নুগেটের বাইরে পেয়েছি, তবে বাস্তবে এটির কোনও RunTaskপদ্ধতি আছে বলে মনে হয় না । সবচেয়ে কাছের জিনিসটি আমি খুঁজে পেতে পারি Runতবে এটির কোনও Resultসম্পত্তি নেই।
আসাদ সাইদুদ্দিন

3
@ আসাদ: হ্যাঁ, 2 বছরেরও বেশি পরে এপিআই পরিবর্তন হয়েছে। আপনি এখন সহজভাবে বলতে পারেনvar result = AsyncContext.Run(MyAsyncMethod);
স্টিফেন ক্লিয়ারি

313

এমন একটি সমাধান যুক্ত করা যা অবশেষে আমার সমস্যার সমাধান করে, আশা করি কারও সময় সাশ্রয় করে।

প্রথমে স্টিফেন ক্লিয়ারির একটি দু'টি নিবন্ধ পড়ুন :

"অ্যাসিঙ্ক কোডটি ব্লক করবেন না" "" দুটি সেরা অনুশীলন "থেকে, প্রথমটি আমার পক্ষে কাজ করেনি এবং দ্বিতীয়টি প্রযোজ্য ছিল না (মূলত যদি আমি ব্যবহার করতে পারি তবে await পারি তবে আমি করব!)।

সুতরাং এখানে আমার কার্যনির্বাহী: কলের ভিতরে কলটি জড়িয়ে রাখুনTask.Run<>(async () => await FunctionAsync()); এবং আশা করা যায় কোনও অচলাবস্থা নেই নেই।

আমার কোডটি এখানে:

public class LogReader
{
    ILogger _logger;

    public LogReader(ILogger logger)
    {
        _logger = logger;
    }

    public LogEntity GetLog()
    {
        Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync());
        return task.Result;
    }

    public async Task<LogEntity> GetLogAsync()
    {
        var result = await _logger.GetAsync();
        // more code here...
        return result as LogEntity;
    }
}

5
দুই বছর পরে, আমি কীভাবে এই সমাধানটি ধরে রাখছি তা জানতে আগ্রহী। কোন খবর? এই পদ্ধতির কি সূক্ষ্মতা রয়েছে যা নতুনদের হারিয়ে গেছে?
ড্যান এস্পারজা

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

29
@ ক্রিসপ্রেট - আপনি ঠিক থাকতে পারেন, কারণ Task.Run()একটি অ্যাসিঙ্ক কোডের মধ্যে সেরা অনুশীলন নয়। তবে, আবার, মূল প্রশ্নের উত্তর কী? কখনও কখনও অ্যাসিঙ্ক পদ্ধতিতে সিঙ্ক্রোনালি কল করবেন না? আমরা ইচ্ছুক, কিন্তু একটি সত্য বিশ্বে, কখনও কখনও আমাদের করতে হবে।
তোহিদ

1
@ তোহিদ আপনি স্টিফেন ক্লিয়ারির লাইব্রেরিটি চেষ্টা করতে পারেন আমি দেখেছি লোকেরা এটি ধরে নিয়েছে এবং Parallel.ForEachঅপব্যবহারের ফলে 'আসল ওয়ার্ল্ড'-এ কোনও প্রভাব পড়বে না এবং শেষ পর্যন্ত এটি সার্ভারগুলিকে নামিয়ে ফেলল। এই কোডটি কনসোল অ্যাপ্লিকেশনগুলির জন্য ঠিক আছে তবে @ ক্রিসারপ্যাট বলেছেন, ওয়েব অ্যাপ্লিকেশনগুলিতে ব্যবহার করা উচিত নয়। এটি "এখন" কাজ করতে পারে তবে স্কেলযোগ্য নয়।
মাখদুমি

1
আমি এটিকে উত্সাহ দেওয়ার জন্য পর্যাপ্ত পয়েন্ট পাওয়ার জন্য নতুন অ্যাকাউন্টস তৈরি করতে শুরু করতে আগ্রহী ....
জিয়ানিস প্যারাস্কেভোপল্লো

206

মাইক্রোসফ্ট অ্যাসিঙ্ককে সিঙ্ক হিসাবে চালানোর জন্য একটি অ্যাসিঙ্কহেল্পার (অভ্যন্তরীণ) শ্রেণি তৈরি করেছিল। উত্সটি দেখে মনে হচ্ছে:

internal static class AsyncHelper
{
    private static readonly TaskFactory _myTaskFactory = new 
      TaskFactory(CancellationToken.None, 
                  TaskCreationOptions.None, 
                  TaskContinuationOptions.None, 
                  TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        return AsyncHelper._myTaskFactory
          .StartNew<Task<TResult>>(func)
          .Unwrap<TResult>()
          .GetAwaiter()
          .GetResult();
    }

    public static void RunSync(Func<Task> func)
    {
        AsyncHelper._myTaskFactory
          .StartNew<Task>(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
}

মাইক্রোসফ্ট.এএসপনেট.এডেন্টিটি বেস ক্লাসগুলিতে কেবল অ্যাসিঙ্ক পদ্ধতি রয়েছে এবং তাদের সিঙ্ক হিসাবে ডাকার জন্য এখানে সম্প্রসারিত পদ্ধতিগুলির মতো শ্রেণীর রয়েছে (উদাহরণস্বরূপ ব্যবহার):

public static TUser FindById<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<TUser>(() => manager.FindByIdAsync(userId));
}

public static bool IsInRole<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId, string role) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<bool>(() => manager.IsInRoleAsync(userId, role));
}

কোডের লাইসেন্সিং শর্তাদি সম্পর্কে উদ্বিগ্নদের জন্য, এখানে খুব অনুরূপ কোডের লিঙ্ক দেওয়া হয়েছে (কেবলমাত্র থ্রেডে সংস্কৃতির জন্য সমর্থন যোগ করা হয়েছে) যা মাইক্রোসফ্ট দ্বারা এমআইটি লাইসেন্সপ্রাপ্ত তা বোঝাতে মন্তব্য রয়েছে। https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs


2
আমার অ্যাসিঙ্ক পদ্ধতিগুলি অন্যান্য অ্যাসিঙ্ক পদ্ধতির জন্য অপেক্ষা করছে। আমি আমার awaitকলগুলির সাথে কোনও সাজাচ্ছি না ConfigureAwait(false)। আমি Global.asax এ AsyncHelper.RunSyncফাংশন থেকে একটি Application_Start()অ্যাসিঙ্ক ফাংশনটি কল করার চেষ্টা করেছি এবং এটি কাজ করে বলে মনে হচ্ছে। এর অর্থ কি এই AsyncHelper.RunSyncপোস্টিংয়ের অন্য কোথাও পড়ার বিষয়ে "মার্শাল কলারের প্রসঙ্গে ফিরে আসা" ডেডলক ইস্যুটির জন্য নির্ভরযোগ্য নয়?
Bob.at.Indigo.Health

1
@ Bob.at.SBS আপনার কোড যা করবে তার উপর নির্ভর করে। এটি এত সহজ নয় যেমন আমি এই কোডটি ব্যবহার করি আমি নিরাপদ । এটি একটি সংক্ষিপ্তভাবে async কমান্ড চালানোর জন্য খুব ন্যূনতম এবং আধা-নিরাপদ উপায়, এটি সহজেই অযৌক্তিকভাবে ডডলকগুলির কারণ হিসাবে ব্যবহার করা যেতে পারে।
এরিক ফিলিপস

1
ধন্যবাদ। ২ টি ফলো-আপ প্রশ্ন: ১) আপনি কি অ্যাসিঙ্ক পদ্ধতিটি কোনও অচলাবস্থার কারণ হতে পারে তা এড়াতে চান এমন একটি উদাহরণ দিতে পারেন, এবং ২) এই প্রসঙ্গে ডেডলকগুলি প্রায়শই সময়-নির্ভর? যদি এটি অনুশীলনে কাজ করে তবে আমার কোডটিতে এখনও কি আমার একটি সময়নির্ভর ডেডলক থাকতে পারে?
Bob.at.Indigo.Health

@ Bob.at.SBS আমি উপরের ডানদিকে প্রশ্ন জিজ্ঞাসা বোতামটি ব্যবহার করে প্রশ্ন জিজ্ঞাসা করার পরামর্শ দেব । আপনি এই প্রশ্নের একটি লিঙ্ক অন্তর্ভুক্ত করতে পারেন বা আপনার প্রশ্নের উত্তর হিসাবে উল্লেখ হিসাবে।
এরিক ফিলিপস

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

150

অ্যাসিঙ্ক মেইন এখন সি # 7.2 এর অংশ এবং প্রজেক্টগুলি উন্নত বিল্ড সেটিংসে সক্ষম হতে পারে।

সি # <7.2 এর জন্য সঠিক উপায়টি হ'ল:

static void Main(string[] args)
{
   MainAsync().GetAwaiter().GetResult();
}


static async Task MainAsync()
{
   /*await stuff here*/
}

আপনি এটি অনেকগুলি মাইক্রোসফ্ট ডকুমেন্টেশনে ব্যবহৃত দেখতে পাবেন, উদাহরণস্বরূপ: https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use- বিষয়-সদস্যতাগুলি


11
কেন কেউ এটিকে ভোট দিয়েছিল তা আমার ধারণা নেই। এটি আমার জন্য কাজ করে মহান। এই সংশোধন না করে, আমাকে সবখানেই প্রচার করতে হবে have
বন্দী জেরো

11
এর চেয়ে ভাল কেন MainAsync().Wait()?
ক্রাশ করুন

8
আমি রাজী. আপনার কেবল মেইনএন্সেঙ্ক () দরকার এই সমস্ত কিছুর পরিবর্তে () অপেক্ষা করুন।
হাজাত

8
@ ক্রাশ আমি বর্ণনা করছিলাম যে এটি কীভাবে কিছু অচলাবস্থা এড়াতে পারে। কিছু পরিস্থিতিতে কল করে .উইআইআই বা এসপ নেট থ্রেড থেকে ওয়েট () অপেক্ষা করা একটি অচলাবস্থার কারণ। অ্যাসিঙ্ক ডেডলকস
ডেভিড

6
@ ক্লিনটিবি: আপনার একেবারে এএসপি.এনইটি কোর এ করা উচিত নয়। ওয়েব অ্যাপ্লিকেশনগুলি বিশেষত থ্রেড-অনাহারে আক্রান্ত হওয়ার জন্য ঝুঁকিপূর্ণ এবং প্রতিবার আপনি এটি করার সময়, আপনি পুল থেকে একটি থ্রেড টানছেন যা অন্যথায় কোনও অনুরোধটি সরবরাহ করার জন্য ব্যবহৃত হবে। এটি ডেস্কটপ / মোবাইল অ্যাপ্লিকেশনগুলির জন্য কম সমস্যাযুক্ত কারণ তারা traditionতিহ্যগতভাবে একক ব্যবহারকারী।
ক্রিস প্র্যাট

52
public async Task<string> StartMyTask()
{
    await Foo()
    // code to execute once foo is done
}

static void Main()
{
     var myTask = StartMyTask(); // call your method which will return control once it hits await
     // now you can continue executing code here
     string result = myTask.Result; // wait for the task to complete to continue
     // use result

}

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


15
Waitব্যতিক্রম মোড়ানো এবং একটি অচলাবস্থা সম্ভাবনা আছে।
স্টিফেন ক্লিয়ারি

আমি ভেবেছিলাম আপনি যদি ব্যবহার না করেই অ্যাসিঙ্ক পদ্ধতিটি কল করেন তবে এটি awaitসিঙ্ক্রোনালি কার্যকর করা হবে। কমপক্ষে এটি আমার জন্য কাজ করে (কল না করে myTask.Wait)। আসলে, আমি কল করার চেষ্টা করার পরে আমি একটি ব্যতিক্রম পেয়েছিলাম myTask.RunSynchronously()কারণ এটি ইতিমধ্যে কার্যকর করা হয়েছিল!
বিস্মিত

2
আমি এই উত্তর পছন্দ। ছোট এবং মার্জিত সম্পাদনার জন্য ভাল মন্তব্য। অবদানের জন্য আপনাকে ধন্যবাদ! আমি এখনও সমঝোতা শিখছি, তাই সবকিছুই সাহায্য করে :)
kayleeFrye_onDeck

2
এই উত্তরটি কি আজও চলবে? আমি এটি কেবলমাত্র একটি এমভিসি রেজার প্রকল্পে চেষ্টা করেছি এবং অ্যাপ্লিকেশনটি অ্যাক্সেস করার জন্য কেবল স্থায়ী .Result
23:24

7
@ ট্র্রু ব্লুউসি এটি সিঙ্ক্রোনাইজেশন প্রসঙ্গে ডেডলক। আপনার অ্যাসিঙ্ক কোড মার্শালগুলি পুনরায় সিঙ্ক্রোনাইজেশন প্রসঙ্গে ফিরে আসে, কিন্তু Resultসেই সময়টি কল দ্বারা অবরুদ্ধ করা হয় , তাই এটি কখনই সেখানে যায় না। এবং Resultকখনই শেষ হয় না, কারণ এটি Resultমূলত শেষের জন্য অপেক্ষা করা এমন কারও জন্য অপেক্ষা করছে : ডি
লুয়ান

40

আমি 100% নিশ্চিত নই, তবে আমি বিশ্বাস করি যে এই ব্লগে বর্ণিত কৌশলটি অনেক পরিস্থিতিতে কাজ করা উচিত:

আপনি task.GetAwaiter().GetResult()যদি সরাসরি এই প্রচারের যুক্তিকে ডাকতে চান তবে আপনি এটি ব্যবহার করতে পারেন।


6
উপরের স্টিফেন ক্লিয়ারির উত্তরের সমাধান এ পদ্ধতিটি ব্যবহার করে। ওয়েটঅ্যান্ডআউনআরউপএক্সেপশন উত্স দেখুন ।
ওরাড

আপনি যে ফাংশনটি কল করছেন তা যদি অকার্যকর বা টাস্ক হয় তবে আপনার কী গেটআরসাল্ট () ব্যবহার করা দরকার? মানে আপনি যদি কোনও ফল ফিরে পেতে না চান
ব্যাটম্যাকি

হ্যাঁ, অন্যথায় এটি টাস্ক শেষ না হওয়া পর্যন্ত অবরুদ্ধ হবে না। বিকল্পভাবে পরিবর্তে GetAwaiter কলিং () GetResult () আপনার .Wait () কল করতে পারেন।
NStuke

1
এটি "অনেক পরিস্থিতিতে" অংশ। এটি সামগ্রিক থ্রেডিং মডেলের উপর নির্ভর করে এবং অচলাবস্থার ঝুঁকি আছে কি না তা নির্ধারণ করতে অন্যান্য থ্রেডগুলি কী করছে।
এনএসটুকে

24

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

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

অ্যাড-হক বার্তা পাম্প সহায়কটির কোড:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Threading
{
    /// <summary>Provides a pump that supports running asynchronous methods on the current thread.</summary>
    public static class AsyncPump
    {
        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static void Run(Action asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(true);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function
                syncCtx.OperationStarted();
                asyncMethod();
                syncCtx.OperationCompleted();

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static void Run(Func<Task> asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(false);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function and alert the context to when it completes
                var t = asyncMethod();
                if (t == null) throw new InvalidOperationException("No task provided.");
                t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
                t.GetAwaiter().GetResult();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Runs the specified asynchronous method.</summary>
        /// <param name="asyncMethod">The asynchronous method to execute.</param>
        public static T Run<T>(Func<Task<T>> asyncMethod)
        {
            if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

            var prevCtx = SynchronizationContext.Current;
            try
            {
                // Establish the new context
                var syncCtx = new SingleThreadSynchronizationContext(false);
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                // Invoke the function and alert the context to when it completes
                var t = asyncMethod();
                if (t == null) throw new InvalidOperationException("No task provided.");
                t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

                // Pump continuations and propagate any exceptions
                syncCtx.RunOnCurrentThread();
                return t.GetAwaiter().GetResult();
            }
            finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
        }

        /// <summary>Provides a SynchronizationContext that's single-threaded.</summary>
        private sealed class SingleThreadSynchronizationContext : SynchronizationContext
        {
            /// <summary>The queue of work items.</summary>
            private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =
                new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
            /// <summary>The processing thread.</summary>
            private readonly Thread m_thread = Thread.CurrentThread;
            /// <summary>The number of outstanding operations.</summary>
            private int m_operationCount = 0;
            /// <summary>Whether to track operations m_operationCount.</summary>
            private readonly bool m_trackOperations;

            /// <summary>Initializes the context.</summary>
            /// <param name="trackOperations">Whether to track operation count.</param>
            internal SingleThreadSynchronizationContext(bool trackOperations)
            {
                m_trackOperations = trackOperations;
            }

            /// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
            /// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
            /// <param name="state">The object passed to the delegate.</param>
            public override void Post(SendOrPostCallback d, object state)
            {
                if (d == null) throw new ArgumentNullException("d");
                m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
            }

            /// <summary>Not supported.</summary>
            public override void Send(SendOrPostCallback d, object state)
            {
                throw new NotSupportedException("Synchronously sending is not supported.");
            }

            /// <summary>Runs an loop to process all queued work items.</summary>
            public void RunOnCurrentThread()
            {
                foreach (var workItem in m_queue.GetConsumingEnumerable())
                    workItem.Key(workItem.Value);
            }

            /// <summary>Notifies the context that no more work will arrive.</summary>
            public void Complete() { m_queue.CompleteAdding(); }

            /// <summary>Invoked when an async operation is started.</summary>
            public override void OperationStarted()
            {
                if (m_trackOperations)
                    Interlocked.Increment(ref m_operationCount);
            }

            /// <summary>Invoked when an async operation is completed.</summary>
            public override void OperationCompleted()
            {
                if (m_trackOperations &&
                    Interlocked.Decrement(ref m_operationCount) == 0)
                    Complete();
            }
        }
    }
}

ব্যবহার:

AsyncPump.Run(() => FooAsync(...));

অ্যাসিঙ্ক পাম্পের আরও বিশদ বিবরণ এখানে পাওয়া যায়


ব্যতিক্রম প্রসঙ্গ এবং AsyncPump stackoverflow.com/questions/23161693/...
PreguntonCojoneroCabrón

এটি কোনও Asp.net দৃশ্যে কাজ করে না, কারণ আপনি এলোমেলোভাবে এইচটিটিপি কনটেক্সট.কেনর্ট হারাতে পারেন।
জোশ মাউচ 18

12

এই প্রশ্নের প্রতি আর কেউ মনোযোগ দিচ্ছেন ...

আপনি যদি Microsoft.VisualStudio.Services.WebApiসেখানে তাকান একটি ক্লাস বলা হয় TaskExtensions। এই শ্রেণীর মধ্যে আপনি স্থির এক্সটেনশন পদ্ধতিটি দেখতে পাবেনTask.SyncResult() , যা টাস্কটি ফিরে না আসা পর্যন্ত থ্রেডকে পুরোপুরি অবরোধ করে।

অভ্যন্তরীণভাবে এটি কল করে task.GetAwaiter().GetResult()যা বেশ সহজ, তবে এটি asyncযে কোনও পদ্ধতিতে ফিরে আসে Task, Task<T>বা এটিতে ওভারলোড হয়Task<HttpResponseMessage> ... সিনট্যাকটিক চিনি, বাচ্চা ... বাবার মিষ্টি দাঁত পেয়েছে তার জন্য অতিরিক্ত চাপ পড়ে।

দেখে মনে হচ্ছে ...GetAwaiter().GetResult()কোনও ব্লকিং প্রসঙ্গে অ্যাসিঙ্ক কোডটি কার্যকর করতে এমএস-অফিসিয়াল উপায়। আমার ব্যবহারের ক্ষেত্রে খুব সূক্ষ্ম কাজ বলে মনে হচ্ছে।


3
আপনি আমাকে "সম্পূর্ণ ব্লকগুলির মতো" করেছেন।
দাউদ ইবনে কেরেম

9
var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);

OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();

অথবা এটি ব্যবহার করুন:

var result=result.GetAwaiter().GetResult().AccessToken

6

আপনি সিঙ্ক্রোনাস কোড থেকে যে কোনও অ্যাসিনক্রোনাস পদ্ধতিতে কল করতে পারেন, এটি হ'ল যতক্ষণ না আপনার awaitসেগুলি চালনা করা দরকার , সেই ক্ষেত্রে সেগুলিও চিহ্নিত করা asyncউচিত।

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

আমি সত্যিই আপনার পদ্ধতিটি তৈরি asyncকরতে পারি না এবং আপনি সিঙ্ক্রোনাস পদ্ধতিটি লক করতে চান না, তারপরে আপনাকে কোনও কলব্যাক পদ্ধতিটি টাস্কে চালিয়ে যাওয়া পদ্ধতিতে প্যারামিটার হিসাবে পাস করে ব্যবহার করতে হবে।


5
তারপরে এখন সেই পদ্ধতিটি সিঙ্ক্রোনসিভাবে কল করা হবে না এটি কি তাই করবে?
জেফ মার্কাডো

2
আমি যেমন বুঝতে পেরেছি, প্রশ্নটি হ'ল আপনি কি অ-অ্যাসিঙ্ক পদ্ধতি থেকে অ্যাসিঙ্ক পদ্ধতিটি কল করতে পারেন। এর দ্বারা অ্যাসিঙ্ক পদ্ধতিটি ব্লকিং পদ্ধতিতে কল করা বোঝায় না।
বেস 2

দুঃখিত, আপনার " asyncসেগুলিও চিহ্নিত করতে হবে" আপনি সত্যই যা বলছিলেন তা থেকে আমার দৃষ্টি আকর্ষণ করে।
জেফ মার্কাডো

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

6

আমি জানি আমি অনেক দেরি করেছি। তবে আমার মতো কেউ এটিকে একটি পরিষ্কার, সহজ উপায়ে এবং অন্য কোনও লাইব্রেরির উপর নির্ভর করে সমাধান করতে চেয়েছিলেন।

রায়ান থেকে নিম্নলিখিত কোডের টুকরাটি পেয়েছি

public static class AsyncHelpers
{
    private static readonly TaskFactory taskFactory = new
        TaskFactory(CancellationToken.None,
            TaskCreationOptions.None,
            TaskContinuationOptions.None,
            TaskScheduler.Default);

    /// <summary>
    /// Executes an async Task method which has a void return value synchronously
    /// USAGE: AsyncUtil.RunSync(() => AsyncMethod());
    /// </summary>
    /// <param name="task">Task method to execute</param>
    public static void RunSync(Func<Task> task)
        => taskFactory
            .StartNew(task)
            .Unwrap()
            .GetAwaiter()
            .GetResult();

    /// <summary>
    /// Executes an async Task<T> method which has a T return type synchronously
    /// USAGE: T result = AsyncUtil.RunSync(() => AsyncMethod<T>());
    /// </summary>
    /// <typeparam name="TResult">Return Type</typeparam>
    /// <param name="task">Task<T> method to execute</param>
    /// <returns></returns>
    public static TResult RunSync<TResult>(Func<Task<TResult>> task)
        => taskFactory
            .StartNew(task)
            .Unwrap()
            .GetAwaiter()
            .GetResult();
}

তাহলে আপনি এটিকে এভাবে কল করতে পারেন

var t = AsyncUtil.RunSync<T>(() => AsyncMethod<T>());

6
উপরের জবাবটি হুবহু মনে হচ্ছে আমি কিছু মিস করছি
ইনকোলেশ

2

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

private ReturnType RunSync()
{
  var task = Task.Run(async () => await myMethodAsync(agency));
  if (task.IsFaulted && task.Exception != null)
  {
    throw task.Exception;
  }

  return task.Result;
}

রিটার্ন টাস্কের সাথে কাজ করে et.গেটওয়েটার ()। গেটআরসাল্ট ();
প্রতি জি

হ্যাঁ, তবে আসল ব্যতিক্রম সম্পর্কে কী?
জিয়া হার্নেক

.আমার ফলাফলগুলি মূলত .GetAwaiter () .GETResult ()
পি

-2

এটি একটি নতুন থ্রেড থেকে কল করা যেতে পারে (থ্রেড পুল থেকে নয়!):

public static class SomeHelperClass
{ 
       public static T Result<T>(Func<T> func)
        {
            return Task.Factory.StartNew<T>(
                  () => func()
                , TaskCreationOptions.LongRunning
                ).Result;
        }
}
...
content = SomeHelperClass.Result<string>(
  () => response.Content.ReadAsStringAsync().Result
  );

-3

এই উইন্ডোজ অ্যাসিঙ্ক পদ্ধতির একটি নিফটি সামান্য পদ্ধতি রয়েছে যা AsTask () বলে। পদ্ধতিটি নিজেকে একটি কার্য হিসাবে ফিরিয়ে আনতে আপনি এটি ব্যবহার করতে পারেন যাতে আপনি নিজে এটিতে অপেক্ষা () কল করতে পারেন।

উদাহরণস্বরূপ, উইন্ডোজ ফোন 8 সিলভারলাইট অ্যাপ্লিকেশনটিতে আপনি নিম্নলিখিতটি করতে পারেন:

private void DeleteSynchronous(string path)
{
    StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
    Task t = localFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask();
    t.Wait();
}

private void FunctionThatNeedsToBeSynchronous()
{
    // Do some work here
    // ....

    // Delete something in storage synchronously
    DeleteSynchronous("pathGoesHere");

    // Do other work here 
    // .....
}

আশাকরি এটা সাহায্য করবে!


-4

আপনি যদি এটি চালাতে চান তবে এটি সিঙ্ক করুন

MethodAsync().RunSynchronously()

3
এই পদ্ধতিটি ঠান্ডা কাজ শুরু করার উদ্দেশ্যে করা হয়েছে। সাধারণত অ্যাসিঙ্ক পদ্ধতিগুলি একটি গরম কাজ দেয়, অন্য কথায় একটি টাস্ক যা ইতিমধ্যে শুরু হয়েছে। RunSynchronously()একটি হট টাস্কের ফলাফলের আহ্বান InvalidOperationException। এই কোডটি দিয়ে এটি ব্যবহার করে দেখুন:Task.Run(() => {}).RunSynchronously();
থিওডর জৌলিয়াস

-5
   //Example from non UI thread -    
   private void SaveAssetAsDraft()
    {
        SaveAssetDataAsDraft();
    }
    private async Task<bool> SaveAssetDataAsDraft()
    {
       var id = await _assetServiceManager.SavePendingAssetAsDraft();
       return true;   
    }
   //UI Thread - 
   var result = Task.Run(() => SaveAssetDataAsDraft().Result).Result;

2
অচলাবস্থা তৈরি করুন। উত্তর মুছে ফেলা ভাল।
প্রিগান্টনকোজনিরো ক্যাব্রন

Task.Run (() => SaveAssetDataAsDraft ()) ফলাফল; - অচলাবস্থা উত্পন্ন করে না
আনুবিস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.