আমি কীভাবে সিঙ্ক্রোনিকভাবে একটি অ্যাসিঙ্ক টাস্ক <T> পদ্ধতি চালাব?


628

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

অ্যাসিঙ্ক পদ্ধতি:

public async Task<Customers> GetCustomers()
{
    return await Service.GetCustomersAsync();
}

সাধারণ ব্যবহার:

public async void GetCustomers()
{
    customerList = await GetCustomers();
}

আমি নিম্নলিখিত ব্যবহার করে চেষ্টা করেছি:

Task<Customer> task = GetCustomers();
task.Wait()

Task<Customer> task = GetCustomers();
task.RunSynchronously();

Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)

আমি এখান থেকে একটি পরামর্শও চেষ্টা করেছিলাম , তবে প্রেরক স্থগিত অবস্থায় থাকলে কাজ করে না।

public static void WaitWithPumping(this Task task) 
{
        if (task == null) throw new ArgumentNullException(“task”);
        var nestedFrame = new DispatcherFrame();
        task.ContinueWith(_ => nestedFrame.Continue = false);
        Dispatcher.PushFrame(nestedFrame);
        task.Wait();
}

কলিং থেকে এখানে ব্যতিক্রম এবং স্ট্যাক ট্রেস দেওয়া হয়েছে RunSynchronously:

System.InvalidOperationException

বার্তা : রানস সিনক্রোনাসলি কোনও প্রতিনিধিকে আনবাউন্ড করা কোনও কাজের জন্য ডাকা যাবে না।

অন্তঃসত্ত্বা : নাল

সূত্র : mscorlib

স্ট্যাকট্রেস :

          at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
   at System.Threading.Tasks.Task.RunSynchronously()
   at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 638
   at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 233
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 597
   at System.Collections.Generic.List`1.ForEach(Action`1 action)
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>d__3b.MoveNext() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 625
   at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object state)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at MyApplication.App.Main() in C:\Documents and Settings\...\MyApplication\obj\Debug\App.g.cs:line 50
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

46
"আমি কীভাবে একটি অ্যাসিঙ্ক পদ্ধতিকে সিঙ্ক্রোনালি কল করতে পারি" - এর সেরা উত্তর হ'ল "না"। এটিকে কাজ করার জন্য বল প্রয়োগ করার চেষ্টা করার হ্যাক রয়েছে তবে তাদের সকলেরই খুব সূক্ষ্ম সমস্যা রয়েছে। পরিবর্তে, ব্যাক আপ করুন এবং কোডটি ঠিক করুন যা আপনাকে এটি করার জন্য "প্রয়োজনীয়" করে তোলে।
স্টিফেন ক্লিয়ারি

57
@ স্টেফেন ক্লিয়ারি সম্পূর্ণরূপে সম্মত হন তবে কখনও কখনও এটি কেবল অনিবার্য হয় যেমন আপনার কোডটি কোনও তৃতীয় পক্ষের এপিআই-র উপর নির্ভরশীল থাকে যা অ্যাসিঙ্ক / অপেক্ষা করে না। এছাড়াও, যদি এমভিভিএম ব্যবহার করার সময় ডাব্লুপিএফ বৈশিষ্ট্যগুলিতে আবদ্ধ হয় তবে এটি অসিঙ্ক / অপেক্ষার অক্ষরে অক্ষরে থাকায় এটি বৈশিষ্ট্যগুলিতে সমর্থিত নয়।
কনটাঙ্গো

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

5
@ স্টেফেনক্রিয়ারি ১) জিনএক্সাস একটি তৃতীয় pt সরঞ্জাম এবং এর উত্স কোডটিতে আমার অ্যাক্সেস নেই; 2) জিনএক্সাসে "ফাংশন" বাস্তবায়নও নেই, তাই আমি বুঝতে পারি না কীভাবে আমি এই ধরণের জিনিস দিয়ে "কলব্যাক" প্রয়োগ করতে পারি। নিশ্চয় এটি সমকালীনভাবে ব্যবহারের চেয়ে আরও শক্ত কাজ হবে Task; 3) আমি জিনএক্সাসকে মঙ্গোডিবি সি # ড্রাইভারের সাথে সংহত করছি , যা কিছু পদ্ধতি কেবলমাত্র
অ্যাসিনক্রোনালিজে

1
@ ইগো: অ্যাসিঙ্ক-সামঞ্জস্যপূর্ণ লক ব্যবহার করুন SemaphoreSlim
স্টিফেন ক্লিয়ারি

উত্তর:


455

এখানে আমি একটি কার্যবিবরণী পেয়েছি যা সমস্ত ক্ষেত্রে (স্থগিত প্রেরকগুলি সহ) জন্য কাজ করে। এটি আমার কোড নয় এবং আমি এখনও এটি সম্পূর্ণরূপে বোঝার জন্য কাজ করছি তবে এটি কার্যকর হয়।

এটি ব্যবহার করে বলা যেতে পারে:

customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());

কোডটি এখান থেকে

public static class AsyncHelpers
{
    /// <summary>
    /// Execute's an async Task<T> method which has a void return value synchronously
    /// </summary>
    /// <param name="task">Task<T> method to execute</param>
    public static void RunSync(Func<Task> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        synch.Post(async _ =>
        {
            try
            {
                await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();

        SynchronizationContext.SetSynchronizationContext(oldContext);
    }

    /// <summary>
    /// Execute's an async Task<T> method which has a T return type synchronously
    /// </summary>
    /// <typeparam name="T">Return Type</typeparam>
    /// <param name="task">Task<T> method to execute</param>
    /// <returns></returns>
    public static T RunSync<T>(Func<Task<T>> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        T ret = default(T);
        synch.Post(async _ =>
        {
            try
            {
                ret = await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();
        SynchronizationContext.SetSynchronizationContext(oldContext);
        return ret;
    }

    private class ExclusiveSynchronizationContext : SynchronizationContext
    {
        private bool done;
        public Exception InnerException { get; set; }
        readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
        readonly Queue<Tuple<SendOrPostCallback, object>> items =
            new Queue<Tuple<SendOrPostCallback, object>>();

        public override void Send(SendOrPostCallback d, object state)
        {
            throw new NotSupportedException("We cannot send to our same thread");
        }

        public override void Post(SendOrPostCallback d, object state)
        {
            lock (items)
            {
                items.Enqueue(Tuple.Create(d, state));
            }
            workItemsWaiting.Set();
        }

        public void EndMessageLoop()
        {
            Post(_ => done = true, null);
        }

        public void BeginMessageLoop()
        {
            while (!done)
            {
                Tuple<SendOrPostCallback, object> task = null;
                lock (items)
                {
                    if (items.Count > 0)
                    {
                        task = items.Dequeue();
                    }
                }
                if (task != null)
                {
                    task.Item1(task.Item2);
                    if (InnerException != null) // the method threw an exeption
                    {
                        throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
                    }
                }
                else
                {
                    workItemsWaiting.WaitOne();
                }
            }
        }

        public override SynchronizationContext CreateCopy()
        {
            return this;
        }
    }
}

28
এটি কীভাবে কাজ করে তার কিছু পটভূমির জন্য, স্টিফেন টুব (মিঃ সমান্তরাল) এ সম্পর্কে একটি সিরিজ পোস্ট লিখেছিলেন। পার্ট 1 পার্ট 2 পার্ট 3
ক্যামেরন ম্যাকফারল্যান্ড

18
আমি ল্যাম্বডাসে মোড়ক ছাড়াই কাজ করার জন্য জনের কোড আপডেট করেছি: github.com/tejacques/AsyncBridge । মূলত আপনি ব্যবহারের বিবৃতি দিয়ে অ্যাসিঙ্ক ব্লকের সাথে কাজ করেন। ব্যবহারের ব্লকের ভিতরে যে কোনও কিছুই শেষের দিকে অপেক্ষা করার সাথে অ্যাসিনক্রোনাক্সভাবে ঘটে। খারাপ দিকটি হ'ল আপনাকে একটি কলব্যাকটিতে নিজেকে টুকরো টুকরো করে ফেলতে হবে, তবে এটি এখনও মোটামুটি মার্জিত, বিশেষত যদি আপনাকে একবারে কয়েকটি অ্যাসিঙ্ক ফাংশন কল করতে হয়।
টম জ্যাক

17
@StephenCleary যদিও আমি সাধারণত আপনার সাথে একমত যে কোড সব পথ নিচে ASYNC হওয়া উচিত, কখনো কখনো আপনি নিজেকে যেখানে এক একটি infeasible অবস্থায় এটি হয়েছে একটি সমকালীন কল যেমন জোর করে। মূলত, আমার পরিস্থিতিটি হচ্ছে আমার সমস্ত ডেটা অ্যাক্সেস কোড অ্যাসিঙ্ক ফ্যাশনে in আমার সাইটম্যাপের উপর ভিত্তি করে একটি সাইটম্যাপ তৈরি করা দরকার এবং আমি যে তৃতীয় পক্ষের লাইব্রেরিটি ব্যবহার করছিলাম সেটি ছিল এমভিসিসাইটম্যাপ। এখন যখন এটি DynamicNodeProviderBaseবেস শ্রেণীর মাধ্যমে প্রসারিত করছে , কেউ এটিকে কোনও asyncপদ্ধতি হিসাবে ঘোষণা করতে পারে না । হয় আমাকে একটি নতুন লাইব্রেরি সহ প্রতিস্থাপন করতে হবে, বা কেবল একটি সিঙ্ক্রোনাস অপকে কল করতে হবে।
justin.lovell

6
@ জাস্টিন.লভেল: হ্যাঁ, গ্রন্থাগারের সীমাবদ্ধতা কমপক্ষে লাইব্রেরিটি আপডেট না হওয়া পর্যন্ত আমাদের হ্যাক লাগাতে বাধ্য করতে পারে। মনে হচ্ছে এমভিসিসাইটম্যাপ এমন একটি পরিস্থিতি যেখানে একটি হ্যাক প্রয়োজন (এমভিসি ফিল্টার এবং শিশু ক্রিয়াগুলিও); আমি কেবলমাত্র লোকদের এ থেকে সাধারণভাবে বিরত করি কারণ যখন প্রয়োজন হয় না তখন এ জাতীয় হ্যাকগুলি প্রায়শই ব্যবহৃত হয় । বিশেষত এমভিসির সাথে, কিছু এএসপি.এনইটি / এমভিসি এপিআই তাদের ধরে রেখেছে তা ধরে নিচ্ছে AspNetSynchronizationContext, সুতরাং আপনি যদি সেই এপিআইগুলিকে কল করেন তবে এই বিশেষ হ্যাকটি কাজ করবে না।
স্টিফেন ক্লিয়ারি

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

318

পরামর্শ দিন এই উত্তরটি তিন বছরের পুরানো। বেশিরভাগ ক্ষেত্রে নেট নেট a.০ এর একটি অভিজ্ঞতার ভিত্তিতে আমি এটি লিখেছিলাম এবং বিশেষত এর সাথে 4.5 async-await। সাধারণভাবে বলতে গেলে এটি একটি দুর্দান্ত সহজ সমাধান তবে এটি কখনও কখনও জিনিসগুলিকে ভেঙে দেয়। মন্তব্যগুলিতে আলোচনাটি পড়ুন।

। নেট 4.5

কেবল এটি ব্যবহার করুন:

// For Task<T>: will block until the task is completed...
var result = task.Result; 

// For Task (not Task<T>): will block until the task is completed...
task2.RunSynchronously();

দেখুন: টাস্কওয়েটার , টাস্ক.সেসাল্ট , টাস্ক.রুনসিংক্রোনাসলি


। নেট 4.0

এটা ব্যবহার কর:

var x = (IAsyncResult)task;
task.Start();

x.AsyncWaitHandle.WaitOne();

...অথবা এটা:

task.Start();
task.Wait();

67
.Resultনির্দিষ্ট দৃশ্যের একটি অচলাবস্থা তৈরি করতে পারে
জর্ডি ল্যাঙ্গেন

122
Resultআমি আমার ব্লগে যেমন বর্ণনা করেছি তেমন কোডে সহজেই অচলাবস্থা তৈরিasync করতে পারে
স্টিফেন ক্লিয়ারি

8
@ স্টেফেনক্রিয়ারি আমি আপনার পোস্টটি পড়েছি এবং নিজে চেষ্টা করেছি। আমি সত্যই বলেছি যে মাইক্রোসফ্টে কেউ সত্যই মাতাল ছিল ... উইনফর্ম এবং ব্যাকগ্রাউন্ড থ্রেডের মতোই এটি একই সমস্যা ....
একে_

9
প্রশ্নটি এমন একটি টাস্ক সম্পর্কিত যা অ্যাসিঙ্ক পদ্ধতিতে ফিরে আসে। টাস্ক ধরনের ইতিমধ্যে শুরু হয়ে থাকতে পারে, মৃত্যুদন্ড কার্যকর করা, বা বাতিল, তাই ব্যবহার Task.RunSynchronously পদ্ধতি হতে পারে InvalidOperationException । এমএসডিএন পৃষ্ঠা দেখুন: টাস্ক.রুনসিনক্রোনাস পদ্ধতি । তদ্ব্যতীত, এই টাস্কটি সম্ভবত টাস্ক.ফ্যাক্টরি.স্টার্টনিউ বা টাস্ক.আর পদ্ধতি দ্বারা তৈরি করা হয়েছে ( অ্যাসিঙ্ক পদ্ধতির অভ্যন্তরে), সুতরাং এটি আবার শুরু করার চেষ্টা করা বিপজ্জনক। কিছু দৌড়ের অবস্থা রানটাইমের সময় ঘটতে পারে। অন্য হাতে, Task.Wait এবং Task.Result এর ফলে আমি অচল হয়ে যেতে পারি।
sgnsajgon

4
রান সিঙ্ক্রোনসিভলি আমার জন্য কাজ করেছে ... আমি জানি না যে আমি কিছু মিস করছি কিনা তবে এটি চিহ্নিত উত্তরটির ভয়াবহতার চেয়ে ভাল মনে হয় - আমি কেবল পরীক্ষার কোডের জন্য অ্যাসিঙ্কটি স্যুইচিংয়ের একটি উপায় খুঁজছিলাম যা কেবল সেখানে থামার জন্য ইউআই ঝুলন্ত থেকে
জোনিআআআআআআআআআআআআআআআআআআআআআআআআআ। আ।

121

অবাক করা কেউ এই উল্লেখ করেনি:

public Task<int> BlahAsync()
{
    // ...
}

int result = BlahAsync().GetAwaiter().GetResult();

এখানে অন্যান্য কয়েকটি পদ্ধতির মতো সুন্দর নয়, তবে এর নিম্নলিখিত সুবিধা রয়েছে:

  • এটি ব্যতিক্রমগুলি গ্রাস করে না (যেমন Wait)
  • এটি কোনও AggregateException(যেমন Result) এ ছোঁড়া কোনও ব্যতিক্রম গুটিয়ে দেবে না
  • উভয়ের জন্যই কাজ করে Taskএবং Task<T>( নিজে চেষ্টা করে দেখুন! )

এছাড়াও, যেহেতু GetAwaiterহাঁস-টাইপযুক্ত, এটি কোনও টাস্ক নয়, একটি অ্যাসিঙ্ক পদ্ধতি (যেমন ConfiguredAwaitableবা YieldAwaitable) থেকে ফিরে আসা যে কোনও অবজেক্টের জন্য কাজ করা উচিত ।


সম্পাদনা করুন: দয়া করে নোট করুন যে এই পদ্ধতির (বা ব্যবহারের .Result) পক্ষে অচলাবস্থার পক্ষে যতক্ষণ .ConfigureAwait(false)অপেক্ষা করা উচিত প্রতিটি সময় যোগ করার বিষয়টি নিশ্চিত না করে, সম্ভবত সমস্ত এ্যাসএনসি পদ্ধতি যা BlahAsync()( সম্ভবত এটি সরাসরি কল করে না) পৌঁছতে পারে । ব্যাখ্যা

// In BlahAsync() body
await FooAsync(); // BAD!
await FooAsync().ConfigureAwait(false); // Good... but make sure FooAsync() and
                                        // all its descendants use ConfigureAwait(false)
                                        // too. Then you can be sure that
                                        // BlahAsync().GetAwaiter().GetResult()
                                        // won't deadlock.

আপনি যদি .ConfigureAwait(false)সর্বত্র সংযুক্ত করতে খুব অলস হন এবং আপনি পারফরম্যান্সের বিষয়ে চিন্তা করেন না তবে আপনি বিকল্পভাবে করতে পারেন

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()

1
সহজ জিনিস জন্য আমার জন্য কাজ করে। এছাড়াও, যদি পদ্ধতিটি একটি আইএএনসিএনওপ্রেশন ফিরিয়ে দেয়, আমাকে প্রথমে এটিকে একটি টাস্কে রূপান্তর করতে হয়েছিল: ব্লাহএ্যাসেন্স ()। এসটাস্ক ()। গেটআওয়েটার ()। গেটআরসাল্ট ();
লি ম্যাকফারসন

3
এটি একটি এসএমএক্স ওয়েব পদ্ধতির অভ্যন্তরে অচলাবস্থার সৃষ্টি করেছিল। তবুও, কোনও টাস্কে পদ্ধতি কলটি মোড়ানো। রুন () এটি কার্যকর করে তুলেছে: টাস্ক.রুন (() => ব্লাহএ্যাসেন্স ()) গেটআউটার ()। গেটআরসাল্ট ()
আগস্টো ব্যারেটো

আমি এই পদ্ধতিরটিকে সিন্ট্যাক্টিকভাবে সেরা পছন্দ করি কারণ এতে ল্যাম্বডাস জড়িত না।
dythim

24
আপনার নিজের কাছে একটি লিঙ্ক প্রবেশ করানোর জন্য দয়া করে অন্য ব্যক্তির উত্তরগুলি সম্পাদনা করবেন না। যদি আপনি বিশ্বাস করেন যে আপনার উত্তরটি ভাল, তবে এটির পরিবর্তে একটি মন্তব্য হিসাবে রেখে দিন।
রাহেল

1
ডকস.মাইক্রোসফট.এইন / ইউএস / ডটনেট / এপি / আই সম্পর্কে বলেছেন GetAwaiter(), "এই পদ্ধতিটি সরাসরি কোডে ব্যবহার না করে সংকলক ব্যবহারকারীর জন্য তৈরি।"
থিওফিলাস

75

থ্রিড পুলটিতে কাজটি চালানো আরও সহজ, সময়সূচীটি এটি সিঙ্ক্রোনালি চালানোর জন্য চালাকি করার চেয়ে। এইভাবে আপনি নিশ্চিত হতে পারেন যে এটি অচলাবস্থা হবে না। প্রসঙ্গটি স্যুইচ করার কারণে পারফরম্যান্স প্রভাবিত হয়।

Task<MyResult> DoSomethingAsync() { ... }

// Starts the asynchronous task on a thread-pool thread.
// Returns a proxy to the original task.
Task<MyResult> task = Task.Run(() => DoSomethingAsync());

// Will block until the task is completed...
MyResult result = task.Result; 

3
তারপরে আপনি টাস্ককে কল করুন Wউইট ()। ডেটা টাইপ হ'ল টাস্ক।
মাইকেল এল পেরি

1
আসুন ধরে নেওয়া যাক ডোসোমিংথিংসিন্যাস () পুরোপুরি দীর্ঘমেয়াদী অ্যাসিঙ্ক পদ্ধতি (অভ্যন্তরীণভাবে এটি একটি দীর্ঘ-চলমান কার্যের জন্য অপেক্ষা করে) তবে এটি তার কলকারীকে দ্রুত একটি প্রবাহ নিয়ন্ত্রণ ফিরিয়ে দেয়, সুতরাং ল্যাম্বডা যুক্তির কাজটিও দ্রুত শেষ হয়। টাস্ক.আরুন () এর ফলাফল টাস্ক <টাস্ক> বা টাস্ক <টাস্ক <>> করতে পারে , সুতরাং আপনি বহিরাগত টাস্কের ফলাফলের জন্য অপেক্ষা করছেন যা দ্রুত সম্পন্ন হয়েছে, তবে অভ্যন্তরীণ টাস্ক (অ্যাসিঙ্ক পদ্ধতিতে দীর্ঘকালীন চাকরীর অপেক্ষার কারণে) এখনও চলছে। উপসংহারগুলি হল যে অ্যাসিঙ্ক পদ্ধতির সুসংগত আচরণ অর্জনের জন্য আমাদের সম্ভবত আন-র্যাপ () পদ্ধতির ব্যবহার করতে হবে (যেমন @ জে। লেনন পোস্টে হয়েছিল)।
sgnsajgon

5
আপনি @ ভুল টাস্ক.আরুনটি টাস্ক.ফ্যাক্টরি.স্টার্টনউয়ের চেয়ে আলাদা যে এটি ইতিমধ্যে ফলাফলটিকে স্বয়ংক্রিয়ভাবে মোড়ানো করে। এই নিবন্ধটি দেখুন ।
জুনটজু

1
আমি কি কেবল Task.Run(DoSomethingAsync)পরিবর্তে লিখতে পারি ? এটি এক স্তরের প্রতিনিধিদের সরিয়ে দেয়।
ygoe

1
হাঁ। বিপরীত দিকে যেতে, যদিও হিসাবে Task<MyResult> task = Task.Run(async () => await DoSomethingAsync());এটি আরও স্পষ্ট এবং @sgnsajgon দ্বারা উদ্বেগকে সম্বোধন করে যে এটি কোনও টাস্ক <টাস্ক <MyResult>> ফিরিয়ে দিতে পারে। টাস্কের সঠিক ওভারলোড.রুনটি যে কোনও উপায়েই নির্বাচন করা হয়েছে, তবে অ্যাসিঙ্ক প্রতিনিধি আপনার অভিপ্রায়টি সুস্পষ্ট করে তুলেছে।
মাইকেল এল পেরি

57

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

সেরা উত্তর আপনি না বিবরণ কি হবে "পরিস্থিতি" উপর নির্ভরশীল সঙ্গে।

এটা কি সম্পত্তি প্রাপ্তি / সেটর? বেশিরভাগ ক্ষেত্রে, "অ্যাসিনক্রোনাস প্রোপার্টি" এর চেয়ে অ্যাসিনক্রোনাস পদ্ধতি থাকা ভাল। (আরও তথ্যের জন্য, অ্যাসিক্রোনাস বৈশিষ্ট্যগুলিতে আমার ব্লগ পোস্টটি দেখুন )।

এটি কি কোনও এমভিভিএম অ্যাপ্লিকেশন এবং আপনি অ্যাসিনক্রোনাস ডেটা বন্ডিং করতে চান? তারপরে অ্যাসিক্রোনাস ডেটা বন্ডিং সম্পর্কিতNotifyTask আমার এমএসডিএন নিবন্ধে বর্ণিত হিসাবে আমার মতো কিছু ব্যবহার করুন

এটা কি কনস্ট্রাক্টর? তারপরে আপনি সম্ভবত একটি অ্যাসিনক্রোনাস কারখানা পদ্ধতি বিবেচনা করতে চান। (আরও তথ্যের জন্য, অ্যাসিক্রোনাস কনস্ট্রাক্টরগুলিতে আমার ব্লগ পোস্টটি দেখুন )।

সিঙ্ক-ওভার-অ্যাসিঙ্ক করার চেয়ে প্রায় সবসময় আরও ভাল উত্তর পাওয়া যায়।

যদি আপনার পরিস্থিতির পক্ষে এটি সম্ভব না হয় (এবং পরিস্থিতি বর্ণনা করে এখানে কোনও প্রশ্ন জিজ্ঞাসা করে আপনি এটি জানেন ), তবে আমি কেবল সিঙ্ক্রোনাস কোড ব্যবহার করার পরামর্শ দেব। সমস্ত পথে অ্যাসিঙ্ক সেরা; সমস্তভাবে সিঙ্ক করা দ্বিতীয়-সেরা। সিঙ্ক-ওভার-অ্যাসিঙ্ক প্রস্তাবিত নয়।

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

সেক্ষেত্রে ব্রাউনফিল্ড asyncবিকাশের বিষয়ে আমার নিবন্ধে বর্ণিত হ্যাকগুলির একটি আপনাকে ব্যবহার করতে হবে :

  • অবরুদ্ধকরণ (যেমন, GetAwaiter().GetResult())। মনে রাখবেন যে এটি অচলাবস্থার কারণ হতে পারে (যেমন আমি আমার ব্লগে বর্ণনা করি)।
  • থ্রেড পুলের থ্রেডে কোড চালনা করা (যেমন, Task.Run(..).GetAwaiter().GetResult())। দ্রষ্টব্য যে এটি কেবল তখনই কাজ করবে যদি অ্যাসিক্রোনাস কোডটি কোনও থ্রেড পুলের থ্রেডে চালানো যায় (যেমন, কোনও UI বা ASP.NET প্রসঙ্গে নির্ভর করে না)।
  • নেস্টেড মেসেজ লুপ হয়। দ্রষ্টব্য যে এটি কেবল তখনই কাজ করবে যদি অ্যাসিঙ্ক্রোনাস কোডটি কেবল একটি একক-থ্রেডযুক্ত প্রসঙ্গটি ধরে নেয়, নির্দিষ্ট প্রসঙ্গের ধরণের নয় (প্রচুর ইউআই এবং এএসপি। নেট কোড কোনও নির্দিষ্ট প্রসঙ্গে প্রত্যাশা করে)।

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

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

হ্যাঁ, তাই কোডটি লেখা খুব শক্ত যা পুনরায় ধারণার মুখে মজবুত। এবং নেস্টেড ম্যাসেজের লুপগুলি আপনাকে পুনঃপ্রেরণের মুখে মজবুত কোড লিখতে বাধ্য করে । এই কেন গৃহীত (এবং সর্বাধিক upvoted) এই প্রশ্নের জন্য উত্তর হয় অত্যন্ত বিপজ্জনক অভ্যাস।

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

আপনি এই কোণে নিজেকে খুঁজে না যদি, আমি ভালো কিছু ব্যবহার সুপারিশ করবে Dispatcher.PushFrameWPF অ্যাপসের জন্য সঙ্গে লুপিং, Application.DoEventsWinForm অ্যাপসের জন্য এবং সাধারণ ক্ষেত্রে জন্য, আমার নিজের AsyncContext.Run


স্টিফেন, আরও একটি অনুরূপ ক্যাসেশন রয়েছে যা আপনি খুব দুর্দান্ত উত্তর প্রদান করেছেন। আপনি কি মনে করেন যে এর মধ্যে একটি অনুলিপি হিসাবে বন্ধ হতে পারে বা অনুরোধটি মার্জ করে বা প্রথমে মেটা নিয়ে আসতে পারে (প্রতিটি কিউতে 200k এর মতামত 200+ ভোট রয়েছে)? পরামর্শ?
আলেক্সি লেভেনকভ

1
@ আলেক্সিলেভেনকভ: কয়েকটি কারণে আমি তা করা ঠিক বোধ করি না: 1) লিঙ্কযুক্ত প্রশ্নের উত্তর মোটামুটি পুরানো। 2) আমি এই বিষয়টিতে একটি সম্পূর্ণ নিবন্ধ লিখেছি যা আমি মনে করি যে কোনও বিদ্যমান এসও কিউ / এ এর ​​চেয়ে সম্পূর্ণ সম্পূর্ণ। 3) এই প্রশ্নের গৃহীত উত্তর অত্যন্ত জনপ্রিয়। ৪) আমি স্বীকৃত উত্তরের তীব্র বিরোধিতা করছি । সুতরাং, এটির দ্বিধা হিসাবে এটি বন্ধ করা ক্ষমতার অপব্যবহার হবে; এটি (বা মার্জ) এর দ্বিধা হিসাবে এটি বন্ধ করা আরও বিপজ্জনক উত্তরকে আরও শক্তিশালী করে তুলবে। আমি এটি হতে দিই, এবং এটি সম্প্রদায়কে ছেড়ে দেব।
স্টিফেন ক্লিয়ারি

ঠিক আছে. আমি এটি কোনও উপায়ে চেয়ে মেটাতে আনতে বিবেচনা করব।
অ্যালেক্সি লেভেনকভ

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

1
গ্রেট। আমি asyncএখন আমার অ্যাপ্লিকেশনটিতে সমস্ত পদ্ধতি প্রয়োগ করতে চলেছি। এবং এটা অনেক। এটি কি কেবল ডিফল্ট হতে পারে না?
ygoe

25

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

সি # 5 এ অ্যাসিঙ্ক পদ্ধতিগুলি কার্যকরভাবে হুডের নীচে টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো করে কাটা দ্বারা চালিত হয় Task। যাইহোক, কাটা কাটা পদ্ধতিগুলি কীভাবে সম্পাদন করে তা awaitঅপারেটরের কাছে প্রেরিত অভিব্যক্তির প্রকারের উপর নির্ভর করে ।

বেশিরভাগ সময়, আপনি awaitটাইপের একটি অভিব্যক্তি ব্যবহার করবেন Task। কার্যটির awaitপ্যাটার্নটির বাস্তবায়নটি "স্মার্ট" কারণ এটি স্থগিত করে SynchronizationContextযা মূলত নিম্নলিখিতটি ঘটায়:

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

এজন্যই আপনি সম্ভবত সমস্যায় পড়ছেন - অ্যাসিঙ্ক পদ্ধতির প্রয়োগটি স্থগিত করা সত্ত্বেও - ডিসপ্যাচারে বাকিটি চালানোর চেষ্টা করছে।

.... ব্যাক আপ! ....

আমাকে প্রশ্ন করতে হবে, আপনি কেন একটি অ্যাসিঙ্ক পদ্ধতিতে সিঙ্ক্রোনালি ব্লক করার চেষ্টা করছেন? এই পদ্ধতিটি কেন এই পদ্ধতিটিকে অবিচ্ছিন্নভাবে ডাকতে চেয়েছিল তার উদ্দেশ্যকে হারাবে। সাধারণভাবে, আপনি যখন awaitকোনও ডিসপ্যাচার বা ইউআই পদ্ধতি ব্যবহার শুরু করেন , আপনি আপনার পুরো ইউআই ফ্লো অ্যাসিঙ্কটি চালু করতে চাইবেন। উদাহরণস্বরূপ, যদি আপনার কলস্ট্যাকটি নিম্নলিখিত কিছু ছিল:

  1. [শীর্ষ] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing()- WPFবা WinFormsকোড
  6. [বার্তা লুপ] - WPFবা WinFormsবার্তা লুপ

তারপরে একবার কোডটি async ব্যবহারে রূপান্তরিত হয়ে গেলে আপনি সাধারণত শেষ হয়ে যাবেন

  1. [শীর্ষ] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing()- WPFবা WinFormsকোড
  6. [বার্তা লুপ] - WPFবা WinFormsবার্তা লুপ

আসলে উত্তর দেওয়া

উপরের অ্যাসিচেল্পার্স ক্লাসটি আসলে কাজ করে কারণ এটি নেস্টেড মেসেজ লুপের মতো আচরণ করে, তবে এটি ডিসপ্যাচারে নিজেই চালিত করার চেষ্টা না করে ডিসপ্যাচারের সাথে নিজস্ব সমান্তরাল যান্ত্রিকটি ইনস্টল করে। এটি আপনার সমস্যার একটাই কার্যকর উপায়।

আর একটি কার্যপ্রণালী হ'ল থ্রেডপুলের থ্রেডে আপনার অ্যাসিঙ্ক পদ্ধতিটি চালানো এবং তারপরে এটি সম্পূর্ণ হওয়ার জন্য অপেক্ষা করুন। এটি করা সহজ - আপনি নিম্নলিখিত স্নিপেট দিয়ে এটি করতে পারেন:

var customerList = TaskEx.RunEx(GetCustomers).Result;

চূড়ান্ত এপিআইটি হবে টাস্ক। রুন (...), তবে সিটিপি সহ আপনার প্রাক্তন প্রত্যয় প্রয়োজন ( এখানে ব্যাখ্যা )।


বিশদ ব্যাখ্যার জন্য +1, তবে TaskEx.RunEx(GetCustomers).Resultস্থগিত প্রেরক থ্রেডে চালিত হয়ে গেলে অ্যাপ্লিকেশনটি স্তব্ধ হয়ে যায়। এছাড়াও, getCustomers () পদ্ধতিটি সাধারণত async চালিত হয়, তবে এক পরিস্থিতিতে এটি সুসংগতভাবে চালানো দরকার, সুতরাং আমি পদ্ধতিটির একটি সিঙ্ক সংস্করণ তৈরি না করেই এটি করার উপায় খুঁজছিলাম।
রাচেল

"কেন আপনি অ্যাসিঙ্ক পদ্ধতিতে সিঙ্ক্রোনালি ব্লক করার চেষ্টা করছেন?" asyncপদ্ধতিগুলি যথাযথভাবে ব্যবহার করার জন্য সর্বদা একটি উপায় রয়েছে ; নেস্টেড লুপগুলি অবশ্যই এড়ানো উচিত।
স্টিফেন ক্লিয়ারি

24

এটা আমার জন্য ভাল কাজ করছে

public static class TaskHelper
{
    public static void RunTaskSynchronously(this Task t)
    {
        var task = Task.Run(async () => await t);
        task.Wait();
    }

    public static T RunTaskSynchronously<T>(this Task<T> t)
    {
        T res = default(T);
        var task = Task.Run(async () => res = await t);
        task.Wait();
        return res;
    }
}

এছাড়াও আপনি ব্যবহার করতে হবে Task.Unwrap আপনার কারণ পদ্ধতি Task.Wait বিবৃতি বাইরের টাস্ক (দ্বারা নির্মিত জন্য অপেক্ষা কারণ Task.Run ,) ভেতরের জন্য নয় অপেক্ষায় রয়েছেন টি কার্য এক্সটেনশন পদ্ধতির প্যারামিটার হিসাবে গৃহীত। আপনার Task.Run পদ্ধতিটি Task <T> নয়, কিন্তু কার্য <টাস্ক <টি>> দেয়। কিছু সাধারণ দৃশ্যে আপনার সমাধানটি টাস্কশেল্ডার অপ্টিমাইজেশনের কারণে কাজ করতে পারে, উদাহরণস্বরূপ, অপেক্ষা করুন অপারেশন চলাকালীন বর্তমান থ্রেডের মধ্যে টাস্কগুলি কার্যকর করতে ট্রাইএক্সেকিউটটাস্কইনলাইন পদ্ধতিটি ব্যবহার করুন using দয়া করে এই উত্তরের মন্তব্যটি দেখুন ।
sgnsajgon

1
এটি সঠিক নয়। টাস্ক.রুন টাস্ক <T> এ ফিরে আসবে। এই ওভারলোডটি
ক্লিমেন্ট

এটি কীভাবে ব্যবহার করার কথা? MyAsyncMethod().RunTaskSynchronously();
ডব্লিউপিএফ-এ

18

সিঙ্ক্রোনালিভাবে এবং ইউআই থ্রেডটি ব্লক না করে আমি টাস্কটি চালানোর সহজতম উপায়টি রানস সিনক্রোনাসলি ব্যবহার করা ():

Task t = new Task(() => 
{ 
   //.... YOUR CODE ....
});
t.RunSynchronously();

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


1
তবে যখন অ্যাসিঙ্ক কোডটি আমাদের প্রয়োজনীয় কিছু ফেরত দেয় তখন আমরা এই পদ্ধতিটি কীভাবে ব্যবহার করতে পারি?
এসস্পার্পোশন

16

বেশিরভাগ ইউনিট পরীক্ষায় বা উইন্ডোজ পরিষেবা বিকাশে আমি কয়েকবার এর মুখোমুখি হয়েছি। বর্তমানে আমি সবসময় এই বৈশিষ্ট্যটি ব্যবহার করি:

        var runSync = Task.Factory.StartNew(new Func<Task>(async () =>
        {
            Trace.WriteLine("Task runSync Start");
            await TaskEx.Delay(2000); // Simulates a method that returns a task and
                                      // inside it is possible that there
                                      // async keywords or anothers tasks
            Trace.WriteLine("Task runSync Completed");
        })).Unwrap();
        Trace.WriteLine("Before runSync Wait");
        runSync.Wait();
        Trace.WriteLine("After runSync Waited");

এটি সহজ, সহজ এবং আমার কোনও সমস্যা ছিল না।


এটিই একমাত্র আমার পক্ষে অচলাবস্থা ছিল না।
আন্দ্রেফিজো

15

আমি এই কোডটি Microsoft.AspNet.Identity.Core উপাদানটিতে পেয়েছি এবং এটি কার্যকর হয়।

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

// Microsoft.AspNet.Identity.AsyncHelper
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
    CultureInfo cultureUi = CultureInfo.CurrentUICulture;
    CultureInfo culture = CultureInfo.CurrentCulture;
    return AsyncHelper._myTaskFactory.StartNew<Task<TResult>>(delegate
    {
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = cultureUi;
        return func();
    }).Unwrap<TResult>().GetAwaiter().GetResult();
}


13

কেবলমাত্র একটি সামান্য নোট - এই পদ্ধতির:

Task<Customer> task = GetCustomers();
task.Wait()

WinRT জন্য কাজ করে।

আমাকে বিস্তারিত বলতে দাও:

private void TestMethod()
{
    Task<Customer> task = GetCustomers(); // call async method as sync and get task as result
    task.Wait(); // wait executing the method
    var customer = task.Result; // get's result.
    Debug.WriteLine(customer.Name); //print customer name
}
public class Customer
{
    public Customer()
    {
        new ManualResetEvent(false).WaitOne(TimeSpan.FromSeconds(5));//wait 5 second (long term operation)
    }
    public string Name { get; set; }
}
private Task<Customer> GetCustomers()
{
    return Task.Run(() => new Customer
    {
        Name = "MyName"
    });
}

তবুও এই পদ্ধতিটি কেবল উইন্ডোজ স্টোর সমাধানের জন্য কাজ করে!

দ্রষ্টব্য: আপনি যদি আপনার পদ্ধতিটিকে অন্যান্য অ্যাসিঙ্ক পদ্ধতির অভ্যন্তরে কল করেন তবে এই পদ্ধতিটি থ্রেডটি নিরাপদ নয় (@ সার্ভিসের মন্তব্য অনুসারে)


আমি এই সমাধানটি ব্যাখ্যা করেছি, সম্পাদনা বিভাগটি পরীক্ষা করে দেখুন।
রেডটিক্স

2
এটি অ্যাসিক্রোনাস পরিস্থিতিতে ডেকে এলে খুব সহজেই অচলাবস্থার ফলাফল ঘটতে পারে।
পরিবেশন করুন

পছন্দ করুন সুতরাং আমি অপেক্ষাটি (টাইমআউট) ব্যবহার করে সঠিক হয়ে উঠতে সাহায্য করতে পারি, তাই না?
RredCat

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

@ সার্ভি দেখে মনে হচ্ছে CancellationTokenআমার সমাধানের জন্য আমাকে প্রয়োগ করতে হবে ।
RredCat

10

আপনার কোডে, আপনার প্রথম কার্য সম্পাদনের জন্য অপেক্ষা করুন তবে আপনি এটি শুরু করেন নি তাই এটি অনির্দিষ্টকালের জন্য অপেক্ষা করে। এটা চেষ্টা কর:

Task<Customer> task = GetCustomers();
task.RunSynchronously();

সম্পাদনা:

আপনি বলেন যে আপনি একটি ব্যতিক্রম পেতে। স্ট্যাক ট্রেস সহ আরও বিশদ পোস্ট করুন।
মনো রয়েছে নিম্নলিখিত পরীক্ষা মামলা:

[Test]
public void ExecuteSynchronouslyTest ()
{
        var val = 0;
        Task t = new Task (() => { Thread.Sleep (100); val = 1; });
        t.RunSynchronously ();

        Assert.AreEqual (1, val);
}

এটি আপনার পক্ষে কাজ করে কিনা তা পরীক্ষা করে দেখুন। যদি এটি না হয় তবে খুব বেশি সম্ভাবনা নেই, আপনার কাছে অ্যাসিঙ্ক সিটিপি-র কিছু অদ্ভুত বিল্ড থাকতে পারে। যদি এটি কাজ করে, আপনি সম্ভবত পরীক্ষা করতে চান যে সংকলকটি ঠিক কী উত্পন্ন করে এবং কীভাবে Taskইনস্ট্যান্টেশনটি এই নমুনা থেকে আলাদা।

সম্পাদনা # 2:

আমি রিফ্লেক্টরের সাথে চেক করেছিলাম যে আপনি বর্ণিত ব্যতিক্রম কখন ঘটে m_actionতা ঘটে null। এটি বিস্ময়কর, তবে আমি Async সিটিপিতে কোনও বিশেষজ্ঞ নই। আমি যেমন বলেছি, আপনি আপনার কোড decompile এবং দেখুন কিভাবে ঠিক করা উচিত Taskকোন instantiated হচ্ছে কিভাবে তার আসা m_actionহয় null


পিএস মাঝেমধ্যে নিম্নবিত্তদের সাথে কী চুক্তি করে? বিশদ যত্ন?


আমি কোডটি আরও স্পষ্ট করার চেষ্টা করেছি এমনটি করার জন্য আমি আমার প্রশ্নটি সামঞ্জস্য করেছি। RunSynchronously এর একটি ত্রুটি প্রদান করে RunSynchronously may not be called on a task unbound to a delegate। এর জন্য সমস্ত ফলাফল চাইনিজ হওয়ায় গুগল কোনও সহায়তা করছে না ...
রাচেল

আমি মনে করি পার্থক্যটি হ'ল আমি টাস্কটি তৈরি করি না এবং পরে এটি চালানোর চেষ্টা করি না। পরিবর্তে, awaitকীওয়ার্ডটি ব্যবহার করার সময় অ্যাসিঙ্ক পদ্ধতি দ্বারা টাস্কটি তৈরি করা হয়। আমার আগের মন্তব্যে পোস্ট করা ব্যতিক্রম হ'ল ব্যতিক্রম আমি পেয়েছি, যদিও গুগল করতে পারি না এবং এর কারণ বা সমাধান খুঁজে পাচ্ছি না এমন কয়েকজনের মধ্যে এটি একটি।
রাচেল

1
asyncএবং asyncকীওয়ার্ডগুলি সিনট্যাক্স চিনির চেয়ে বেশি কিছু নয়। সংকলক কোডটি তৈরি করতে কোড তৈরি Task<Customer>করে GetCustomers()যাতে সেখানে আমি প্রথমে দেখতে চাই। ব্যতিক্রম হিসাবে, আপনি কেবল ব্যতিক্রম বার্তা পোস্ট করেছেন, যা ব্যতিক্রম প্রকার এবং স্ট্যাক ট্রেস ছাড়াই অকেজো। ToString()প্রশ্নে ব্যতিক্রমের পদ্ধতি এবং পোস্ট আউটপুট কল করুন ।
ড্যান আব্রামভ

@ গ্রেয়ারন: আমি আমার মূল প্রশ্নে ব্যতিক্রমের বিবরণ এবং স্ট্যাক ট্রেস পোস্ট করেছি।
রাচেল

2
@ গ্রেয়ার আমার মনে হয় আপনি ডাউনটা পেয়েছেন কারণ আপনার পোস্টটি প্রশ্নের ক্ষেত্রে প্রযোজ্য নয়। আলোচনাটি অ্যাসিঙ্ক-অপেক্ষার পদ্ধতিগুলি সম্পর্কে, সাধারণ টাস্ক-রিটার্নিং পদ্ধতি সম্পর্কে নয়। তদুপরি, আমার মতে, অ্যাসিঙ্ক-অপেক্ষার প্রক্রিয়াটি একটি সিনট্যাক্স চিনি, তবে এত তুচ্ছ নয় - এখানে ধারাবাহিকতা, প্রসঙ্গ ক্যাপচারিং, স্থানীয় প্রসঙ্গ পুনরায় শুরুকরণ, স্থানীয় ব্যাতিক্রম ব্যতিক্রমগুলি পরিচালনা এবং আরও অনেক কিছু রয়েছে। তারপরে, অ্যাসিঙ্ক পদ্ধতির ফলাফলের জন্য আপনার রান সিংক্রোনাস পদ্ধতিটি চালু করা উচিত নয় , কারণ সংজ্ঞা অনুসারে অ্যাসিঙ্ক্রোনাস পদ্ধতিটি বর্তমানে কমপক্ষে নির্ধারিত টাস্কটি ফিরিয়ে আনতে হবে এবং একাধিকবার চলমান অবস্থায় রয়েছে।
sgnsajgon

9

নেট 4.6 পরীক্ষিত। এটি অচলাবস্থা এড়াতে পারে।

অ্যাসিঙ্ক পদ্ধতিটি ফেরার জন্য Task

Task DoSomeWork();
Task.Run(async () => await DoSomeWork()).Wait();

অ্যাসিঙ্ক পদ্ধতিটি ফেরার জন্য Task<T>

Task<T> GetSomeValue();
var result = Task.Run(() => GetSomeValue()).Result;

সম্পাদন করা :

যদি কলার থ্রেড পুলের থ্রেডে চলমান থাকে (বা কলার কোনও কার্যক্রমেও থাকে) তবে এটি এখনও কিছু পরিস্থিতিতে অচলাবস্থার কারণ হতে পারে।


1
প্রায় 8 বছর পরে আমার আনসার :) দ্বিতীয় উদাহরণ - মূলত ব্যবহৃত সমস্ত নির্ধারিত প্রসঙ্গে একটি অচলাবস্থা তৈরি করবে (কনসোল অ্যাপ /। নেট কোর / ডেস্কটপ অ্যাপ / ...)। আমি এখন যে বিষয়ে কথা বলছি তা এখানে আপনার আরও সংক্ষিপ্ত বিবরণ রয়েছে: मध्यम.com
ডাব্লু 92

Resultআপনি যদি একটি সিঙ্ক্রোনাস কল চান এবং অন্যথায় একেবারে বিপজ্জনক হন তবে কাজের জন্য উপযুক্ত perfect নামে Resultবা এর ইন্টেলিজেন্সে কিছুই নেই Resultযা ইঙ্গিত করে যে এটি একটি ব্লকিং কল। এটি সত্যই নামকরণ করা উচিত।
জোডম্যান

5

কোড স্নিপ নীচে ব্যবহার করুন

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));

4

কেন এমন কল তৈরি করবেন না:

Service.GetCustomers();

এটা async না।


4
আমি যদি এই কাজটি না পেতে পারি তবে আমি যা করব তা হচ্ছে ... অ্যাসিঙ্ক সংস্করণ ছাড়াও একটি সিঙ্ক সংস্করণ তৈরি করুন
র্যাচেল

3

এই উত্তরটি .NET 4.5 এর জন্য যারা ডাব্লুপিএফ ব্যবহার করছে তাদের জন্য ডিজাইন করা হয়েছে।

আপনি যদি Task.Run()জিইউআই থ্রেডে চালিত করার চেষ্টা করেন task.Wait()তবে আপনার যদি না থাকে তবে অনির্দিষ্টকালের জন্য স্তব্ধ হয়ে যাবেasync আপনার ফাংশন সংজ্ঞায় কীওয়ার্ড ।

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

এই শ্রেণিটি async / প্রতীক্ষিত বিশ্ব এবং অ-সংমিশ্রিত / প্রতীক্ষিত বিশ্বের মধ্যে আঠালো হিসাবে কাজ করতে পারে, এমন পরিস্থিতিতে যেখানে এটি অনিবার্য, যেমন এমভিভিএম বৈশিষ্ট্য বা অন্যান্য APIগুলির উপর নির্ভরতা যা অ্যাসিঙ্ক / প্রতীক্ষা করে না।

/// <summary>
///     Intent: runs an async/await task synchronously. Designed for use with WPF.
///     Normally, under WPF, if task.Wait() is executed on the GUI thread without async
///     in the function signature, it will hang with a threading deadlock, this class 
///     solves that problem.
/// </summary>
public static class TaskHelper
{
    public static void MyRunTaskSynchronously(this Task task)
    {
        if (MyIfWpfDispatcherThread)
        {
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E99213. Task did not run to completion.");
            }
        }
        else
        {
            task.Wait();
            if (task.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E33213. Task did not run to completion.");
            }
        }
    }

    public static T MyRunTaskSynchronously<T>(this Task<T> task)
    {       
        if (MyIfWpfDispatcherThread)
        {
            T res = default(T);
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { res = await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E89213. Task did not run to completion.");
            }
            return res;
        }
        else
        {
            T res = default(T);
            var result = Task.Run(async () => res = await task);
            result.Wait();
            if (result.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E12823. Task did not run to completion.");
            }
            return res;
        }
    }

    /// <summary>
    ///     If the task is running on the WPF dispatcher thread.
    /// </summary>
    public static bool MyIfWpfDispatcherThread
    {
        get
        {
            return Application.Current.Dispatcher.CheckAccess();
        }
    }
}

3

কেবল মন্তব্য করা .Result;বা .Wait()ডেডলকগুলির জন্য ঝুঁকি হিসাবে অনেকে মন্তব্য করেছেন। যেহেতু আমরা বেশিরভাগ অনেলাইনারদের পছন্দ করি আপনি এগুলি ব্যবহার করতে পারেন.Net 4.5<

একটি অ্যাসিঙ্ক পদ্ধতির মাধ্যমে একটি মান অর্জন করা:

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

সুসংগতভাবে একটি অ্যাসিঙ্ক পদ্ধতিতে কল করা হচ্ছে

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

কোনও অচলাবস্থার সমস্যা ব্যবহারের কারণে ঘটবে না Task.Run

সূত্র:

https://stackoverflow.com/a/32429753/3850405


1

আমি মনে করি যে নিম্নলিখিত সাহায্যকারী পদ্ধতিটিও সমস্যার সমাধান করতে পারে।

private TResult InvokeAsyncFuncSynchronously<TResult>(Func< Task<TResult>> func)
    {
        TResult result = default(TResult);
        var autoResetEvent = new AutoResetEvent(false);

        Task.Run(async () =>
        {
            try
            {
                result = await func();
            }
            catch (Exception exc)
            {
                mErrorLogger.LogError(exc.ToString());
            }
            finally
            {
                autoResetEvent.Set();
            }
        });
        autoResetEvent.WaitOne();

        return result;
    }

নিম্নলিখিত উপায়ে ব্যবহার করা যেতে পারে:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);

1
দয়া করে
ভোটিংটি

2
... আমি এখনও আগ্রহ নিয়ে আগ্রহী কেন এই উত্তরটি কেন ভোট দেওয়া হল?
donttellya

এটি সত্য "সংলগ্ন" নয় Y আপনি দুটি থ্রেড তৈরি করেন এবং অন্যটির প্রথম ফলাফলের জন্য অপেক্ষা করুন।
টিএমটি

এবং সব কিছু বাদ দিয়ে, এটি খুব খারাপ ধারণা।
ড্যান প্যান্ট্রি

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

0

এটি আমার পক্ষে কাজ করে

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

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

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

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

    class SomeClass
    {
        public async Task<object> LoginAsync(object loginInfo)
        {
            return await Task.FromResult(0);
        }
        public object Login(object loginInfo)
        {
            return AsyncHelper.RunSync(() => LoginAsync(loginInfo));
            //return this.LoginAsync(loginInfo).Result.Content;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var someClass = new SomeClass();

            Console.WriteLine(someClass.Login(1));
            Console.ReadLine();
        }
    }
}

-1

আমি খুঁজে পেয়েছি যে স্পিনওয়েট এটির জন্য বেশ ভাল কাজ করে।

var task = Task.Run(()=>DoSomethingAsyncronous());

if(!SpinWait.SpinUntil(()=>task.IsComplete, TimeSpan.FromSeconds(30)))
{//Task didn't complete within 30 seconds, fail...
   return false;
}

return true;

উপরোক্ত পদ্ধতির ব্যবহারের প্রয়োজন নেই। ফলাফল বা। ওয়েট ()। এটি আপনাকে একটি সময়সীমা নির্দিষ্ট করতে দেয় যাতে টাস্কটি কখনই শেষ না করে আপনি চিরতরে আটকে যান না।


1
ডাউনভোটটি পরামর্শ দেয় যে কেউ এই পদ্ধতি পছন্দ করেন না। এমন কেউ আছে যে এর খারাপ দিক নিয়ে মন্তব্য করতে পারে?
Grax32

ডাউনওয়েটারের অনুপস্থিতিতে কেন ডাউনটা দেওয়া হয়েছিল, কেউ কি এটিকে উজ্জীবিত করতে পারে? :-)
কার্টিস

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

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

-3

ডাব্লুপি 8-তে:

এটি মোড়ানো:

Task GetCustomersSynchronously()
{
    Task t = new Task(async () =>
    {
        myCustomers = await GetCustomers();
    }
    t.RunSynchronously();
}

ডাকা:

GetCustomersSynchronously();

3
নাহ, এটি কাজ করবে না, কারণ কাজটি নির্মাণকারীর কাছ থেকে প্রতিনিধিটির জন্য অপেক্ষা করে না (এটি একটি প্রতিনিধি এবং কোনও কাজ নয় ..)
রিকো সুটার

-4
    private int GetSync()
    {
        try
        {
            ManualResetEvent mre = new ManualResetEvent(false);
            int result = null;

            Parallel.Invoke(async () =>
            {
                result = await SomeCalcAsync(5+5);
                mre.Set();
            });

            mre.WaitOne();
            return result;
        }
        catch (Exception)
        {
            return null;
        }
    }

-5

অথবা আপনি কেবল সাথে যেতে পারেন:

customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;

এটি সংকলন করার জন্য নিশ্চিত করুন আপনি এক্সটেনশন সমাবেশটি উল্লেখ করেছেন:

System.Net.Http.Formatting

-9

কোডটি অনুসরণ করে এটি আমার জন্য কাজ করে দেখুন:

public async void TaskSearchOnTaskList (SearchModel searchModel)
{
    try
    {
        List<EventsTasksModel> taskSearchList = await Task.Run(
            () => MakeasyncSearchRequest(searchModel),
            cancelTaskSearchToken.Token);

        if (cancelTaskSearchToken.IsCancellationRequested
                || string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
        {
            return;
        }

        if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
        {
            RunOnUiThread(() => {
                textViewNoMembers.Visibility = ViewStates.Visible;                  
                taskListView.Visibility = ViewStates.Gone;
            });

            taskSearchRecureList = null;

            return;
        }
        else
        {
            taskSearchRecureList = TaskFooterServiceLayer
                                       .GetRecurringEvent(taskSearchList);

            this.SetOnAdapter(taskSearchRecureList);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.