সিঙ্ক্রোনালি একটি অ্যাসিঙ্ক অপারেশনের জন্য অপেক্ষা করছে এবং কেন এখানে অপেক্ষা করুন () প্রোগ্রামটি স্থির করে দেয়


318

ভূমিকা : আমি একটি সমাধান খুঁজছি, কেবল একটি সমাধান নয়। সমাধানটি আমি ইতিমধ্যে জানি।

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

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

এটি অ্যাসিনক্রোনাস লগিংয়ের মূল পদ্ধতি:

private async Task WriteToLogAsync(string text)
{
    StorageFolder folder = ApplicationData.Current.LocalFolder;
    StorageFile file = await folder.CreateFileAsync("log.log",
        CreationCollisionOption.OpenIfExists);
    await FileIO.AppendTextAsync(file, text,
        Windows.Storage.Streams.UnicodeEncoding.Utf8);
}

এখন সম্পর্কিত সিঙ্ক্রোনাস পদ্ধতি ...

সংস্করণ 1 :

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.Wait();
}

এটি সঠিক দেখাচ্ছে তবে এটি কার্যকর হয় না। পুরো প্রোগ্রামটি চিরকালের জন্য হিমশীতল।

সংস্করণ 2 :

হুম .. টাস্ক শুরু হতেই পারে না?

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.Start();
    task.Wait();
}

এই ছুড়ে ফেলে InvalidOperationException: Start may not be called on a promise-style task.

সংস্করণ 3:

হুম .. Task.RunSynchronouslyআশাব্যঞ্জক শোনাচ্ছে।

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.RunSynchronously();
}

এই ছুড়ে ফেলে InvalidOperationException: RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.

সংস্করণ 4 (সমাধান):

private void WriteToLog(string text)
{
    var task = Task.Run(async () => { await WriteToLogAsync(text); });
    task.Wait();
}

এইটা কাজ করে. সুতরাং, 2 এবং 3 টি ভুল সরঞ্জাম। কিন্তু 1? 1 এর সাথে কী হয়েছে এবং 4 এর পার্থক্য কী? কী কারণে 1 হিমায়িত হয়ে যায়? টাস্ক অবজেক্টে কিছু সমস্যা আছে? একটি অ-সুস্পষ্ট অচলাবস্থা আছে?


কোন ভাগ্য অন্য কোথাও ব্যাখ্যা পেয়ে? নীচের উত্তরগুলি সত্যই অন্তর্দৃষ্টি দেয় না। আমি প্রকৃতপক্ষে .net 4.0 ব্যবহার করছি 4.5 / 5 নয় তাই আমি কিছু অপারেশন ব্যবহার করতে পারি না তবে একই সমস্যাগুলিতে চলে।
amadib

3
@ আমাদিব, ভার্চ 1 এবং 4 টি [rpvided উত্তরে ব্যাখ্যা করা হয়েছে] ইতিমধ্যে শুরু টাস্কটি আবার শুরু করার চেষ্টা করুন। আপনার প্রশ্ন পোস্ট করুন। এটা তোলে স্পষ্ট নয় কিভাবে আপনি .NET 4.0 উপর .NET 4.5 ASYNC / অপেক্ষায় রয়েছেন সমস্যা থাকতে পারে
গেন্নাদি Vanin Геннадий Ванин

1
সংস্করণ 4 হ'ল জামারিন ফর্মগুলির জন্য সেরা বিকল্প। আমরা বাকি বিকল্পগুলি চেষ্টা করেছিলাম এবং সমস্ত ক্ষেত্রেই কাজ করেছি এবং অভিজ্ঞ অচলাবস্থার অভিজ্ঞতা নেই
রামকৃষ্ণ

ধন্যবাদ! সংস্করণ 4 আমার জন্য কাজ করেছে। কিন্তু এটি কি এখনও অ্যাসিক্রোনাসিকভাবে চালিত হয়? আমি ধরে নিচ্ছি কারণ অ্যাসিঙ্ক কীওয়ার্ডটি আছে।
sshirley

উত্তর:


189

awaitআপনার অ্যাসিঙ্ক্রোনাস পদ্ধতি ভিতরে UI 'তে থ্রেডে ফিরে আসা করার চেষ্টা করছে।

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

Task.Run()সমস্যা সমাধানের জন্য অ্যাসিঙ্ক কলটি সরানো ।
যেহেতু অ্যাসিঙ্ক কলটি এখন একটি থ্রেড পুলের থ্রেডে চলছে, এটি ইউআই থ্রেডে ফিরে আসার চেষ্টা করে না এবং তাই সমস্ত কিছুই কাজ করে।

বিকল্পভাবে, আপনি StartAsTask().ConfigureAwait(false)পুরোপুরি অচলতাকে এড়িয়ে গিয়ে ইউআই থ্রেডের পরিবর্তে থ্রেড পুলটিতে ফিরে আসার জন্য অভ্যন্তরীণ অপারেশনটির অপেক্ষার আগে আপনি কল করতে পারেন।


9
+1 টি। এখানে আরও একটি ব্যাখ্যা দেওয়া হয়েছে - ওউইট এবং ইউআই, এবং ডেডলকস! আহারে!
আলেক্সি লেভেনকোভ

13
ConfigureAwait(false)এই ক্ষেত্রে উপযুক্ত সমাধান। যেহেতু ক্যাপচারিত প্রসঙ্গে কলব্যাকগুলি কল করার দরকার নেই, এটি করা উচিত নয়। একটি এপিআই পদ্ধতি হওয়ায় এটি সমস্ত কলকারীকে ইউআই প্রসঙ্গে বাইরে যেতে বাধ্য করার পরিবর্তে এটি অভ্যন্তরীণভাবে পরিচালনা করা উচিত।
পরিবেশন করুন

আপনি সার্ভিসটি জিজ্ঞাসা করছেন যেহেতু আপনি কনফিগারআউটকে উল্লেখ করেছেন। আমি .net3.5 ব্যবহার করছি এবং আমি যে কনফিগারটি অপেক্ষা করছিলাম তা সরিয়ে ফেলতে হয়েছিল যা আমি ব্যবহার করছি এমন এসিঙ্ক লাইব্রেরিতে পাওয়া যায় না। আমি কীভাবে নিজের লিখতে পারি বা আমার এসিঙ্ক কলটির অপেক্ষার আরও একটি উপায় আছে। আমার পদ্ধতিটিও স্তব্ধ। আমার কাছে টাস্ক নেই তবে টাস্ক নেই un রুন। এই শব্দটি সম্ভবত এটি নিজেই একটি প্রশ্ন হতে পারে।
ফ্লেক্সিক্সিত

@ ফ্লেক্সক্সিক্সিট: আপনার ব্যবহার করা উচিত Microsoft.Bcl.Async
এসএলএক্স

48

asyncসিঙ্ক্রোনাস কোড থেকে কলিং কোড বেশ জটিল হতে পারে।

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

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

এটি এড়ানোর জন্য গাইডলাইনগুলি হ'ল:

  1. ConfigureAwait(continueOnCapturedContext: false)যতটা সম্ভব ব্যবহার করুন । এটি আপনার asyncপদ্ধতিগুলিকে প্রসঙ্গে পুনরায় প্রবেশ না করে চালানো চালিয়ে যেতে সক্ষম করে।
  2. asyncসমস্ত উপায় ব্যবহার করুন । এর awaitপরিবর্তে Resultবা ব্যবহার করুন Wait

যদি আপনার পদ্ধতিটি প্রাকৃতিকভাবে অ্যাসিনক্রোনাস হয় তবে আপনার (সম্ভবত) কোনও সিনক্রোনাস মোড়ক উন্মোচন করা উচিত নয়


আমাকে একটি ক্যাচ () এ এসিঙ্ক টাস্ক সম্পাদন asyncকরতে হবে যা আমি কীভাবে এটি করব এবং আগুন প্রতিরোধ এবং পরিস্থিতি ভুলে যাওয়া সমর্থন করে না ।
জাপানোলিকিকা

1
@ জাপানোলজিকা: ভিএস ২০১৫ অনুসারে ব্লকগুলিতে awaitসমর্থিত catch। আপনি যদি কোনও পুরানো সংস্করণে থাকেন তবে আপনি একটি স্থানীয় ভেরিয়েবলের ব্যতিক্রম নির্ধারণ করতে পারেন এবং awaitক্যাচ ব্লকের পরে এটি করতে পারেন
স্টিফেন ক্লিয়ারি

5

এখানে আমি কি করেছি

private void myEvent_Handler(object sender, SomeEvent e)
{
  // I dont know how many times this event will fire
  Task t = new Task(() =>
  {
    if (something == true) 
    {
        DoSomething(e);  
    }
  });
  t.RunSynchronously();
}

দুর্দান্ত কাজ করছে এবং ইউআই থ্রেড ব্লক করছে না


0

ছোট কাস্টম সিঙ্ক্রোনাইজেশন প্রসঙ্গে, সিঙ্ক ফাংশন অচলিত কাজটি সমাপ্তির জন্য অপেক্ষা করতে পারে, ডেডলক তৈরি না করেই। উইনফর্মস অ্যাপ্লিকেশনটির জন্য এখানে ছোট উদাহরণ।

Imports System.Threading
Imports System.Runtime.CompilerServices

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        SyncMethod()
    End Sub

    ' waiting inside Sync method for finishing async method
    Public Sub SyncMethod()
        Dim sc As New SC
        sc.WaitForTask(AsyncMethod())
        sc.Release()
    End Sub

    Public Async Function AsyncMethod() As Task(Of Boolean)
        Await Task.Delay(1000)
        Return True
    End Function

End Class

Public Class SC
    Inherits SynchronizationContext

    Dim OldContext As SynchronizationContext
    Dim ContextThread As Thread

    Sub New()
        OldContext = SynchronizationContext.Current
        ContextThread = Thread.CurrentThread
        SynchronizationContext.SetSynchronizationContext(Me)
    End Sub

    Dim DataAcquired As New Object
    Dim WorkWaitingCount As Long = 0
    Dim ExtProc As SendOrPostCallback
    Dim ExtProcArg As Object

    <MethodImpl(MethodImplOptions.Synchronized)>
    Public Overrides Sub Post(d As SendOrPostCallback, state As Object)
        Interlocked.Increment(WorkWaitingCount)
        Monitor.Enter(DataAcquired)
        ExtProc = d
        ExtProcArg = state
        AwakeThread()
        Monitor.Wait(DataAcquired)
        Monitor.Exit(DataAcquired)
    End Sub

    Dim ThreadSleep As Long = 0

    Private Sub AwakeThread()
        If Interlocked.Read(ThreadSleep) > 0 Then ContextThread.Resume()
    End Sub

    Public Sub WaitForTask(Tsk As Task)
        Dim aw = Tsk.GetAwaiter

        If aw.IsCompleted Then Exit Sub

        While Interlocked.Read(WorkWaitingCount) > 0 Or aw.IsCompleted = False
            If Interlocked.Read(WorkWaitingCount) = 0 Then
                Interlocked.Increment(ThreadSleep)
                ContextThread.Suspend()
                Interlocked.Decrement(ThreadSleep)
            Else
                Interlocked.Decrement(WorkWaitingCount)
                Monitor.Enter(DataAcquired)
                Dim Proc = ExtProc
                Dim ProcArg = ExtProcArg
                Monitor.Pulse(DataAcquired)
                Monitor.Exit(DataAcquired)
                Proc(ProcArg)
            End If
        End While

    End Sub

     Public Sub Release()
         SynchronizationContext.SetSynchronizationContext(OldContext)
     End Sub

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