क्या “DoSomethingAsync ()” का इंतजार करना संभव है


108

क्या नियमित रूप से चलने वाले अवरोधक (जैसे "उपज वापसी") "एसिंक्स" और "प्रतीक्षा" के साथ असंगत हैं?

यह एक अच्छा विचार देता है कि मैं क्या करने की कोशिश कर रहा हूं:

async Task<IEnumerable<Foo>> Method(String [] Strs)
{
    // I want to compose the single result to the final result, so I use the SelectMany
    var finalResult = UrlStrings.SelectMany(link =>   //i have an Urlstring Collection 
                   await UrlString.DownLoadHtmlAsync()  //download single result; DownLoadHtmlAsync method will Download the url's html code 
              );
     return finalResult;
}

हालांकि, मुझे "संसाधनों से संदेश स्ट्रिंग लोड करने में असमर्थ" का हवाला देते हुए एक संकलक त्रुटि मिलती है।

यहाँ एक और प्रयास है:

async Task<IEnumerable<Foo>> Method(String [] Strs)
{
    foreach(var str in strs)
    {
        yield return await DoSomethingAsync( str)
    }
}

लेकिन फिर से, कंपाइलर एक त्रुटि देता है: "संसाधनों से संदेश स्ट्रिंग लोड करने में असमर्थ"।


यहाँ मेरी परियोजना में वास्तविक प्रोग्रामिंग कोड है

यह बहुत उपयोगी है जब मेरे पास एक सूची कार्य है, उस कार्य को एचटीएमएल एक यूआरएल से डाउनलोड किया जा सकता है और मैं वाक्यविन्यास "उपज वापसी प्रतीक्षा कार्य" का उपयोग करता हूं, परिणाम मुझे चाहिए IEnumerable<Foo>। मैं यह कोड नहीं लिखना चाहता:

async Task<IEnumerable<String>> DownLoadAllURL(String [] Strs)
{
    List<Foo> htmls= new ...
    foreach(var str in strs)
    {
        var html= await DownLoadHtmlAsync( str)
        htmls.Add(item)
    }
    return htmls;
}

लेकिन ऐसा लगता है कि मुझे करना है।

किसी भी मदद के लिए धन्यवाद।


4
यह प्रश्न विशेष रूप से स्पष्ट नहीं है, आपको इस बारे में विस्तार से बता देना चाहिए कि यह वही है जो आप करना चाहते हैं - यह निर्धारित करना मुश्किल है कि आप केवल एक कोड नमूने से क्या करना चाहते हैं।
जस्टिन

3
Ix_Experimental-Async NuGet पैकेज "अतुल्यकालिक प्रगणक" LINQ समर्थन के साथ पूरा भी शामिल है। वे IAsyncEnumerator<T>आर्ने द्वारा परिभाषित प्रकार का उपयोग करते हैं ।
स्टीफन क्लीयर

@StephenCleary उस पैकेज को हटा दिया गया है। चूंकि यह युग था और MS के rxteam द्वारा लिखा गया था, क्या आप जानते हैं कि क्या यह RX में लुढ़का हुआ है?
जोब्रेकहॉस

@JoeBrockhaus: ऐसा लगता है कि यह Ix-Async पर रहता है
स्टीफन क्लीयर

जवाबों:


72

आप जो वर्णन कर रहे हैं वह Task.WhenAllविधि से पूरा किया जा सकता है । ध्यान दें कि कोड एक साधारण एक-लाइनर में कैसे बदल जाता है। क्या होता है कि प्रत्येक व्यक्ति url डाउनलोड करना शुरू कर देता है और फिर WhenAllउन परिचालनों को एक एकल में संयोजित करता है, Taskजिनका इंतजार किया जा सकता है।

Task<IEnumerable<string>> DownLoadAllUrls(string[] urls)
{
    return Task.WhenAll(from url in urls select DownloadHtmlAsync(url));
}

10
इस मामले में async / प्रतीक्षा की आवश्यकता नहीं है। बस asyncविधि से निकालें और return Task.WhenAllसीधे करें।
लूसीसुबल

22
अंतिम पंक्ति को और अधिक स्पष्ट रूप से लिखा जा सकता हैurls.Select(DownloadHtmlAsync)
ब्लूराजा - डैनी पफ्लुगुएफ्ट

1
यह ठीक वही समस्या थी जिसका मैं सामना कर रहा था (URI की एक श्रृंखला से आलसी डाउनलोडिंग स्ट्रिंग)। धन्यवाद।
जेम्स को

13
यह वास्तव में इस सवाल का जवाब नहीं देता है कि Is it possible to await yield? आपको इस एक उपयोग-मामले के लिए एक काम के आसपास मिल गया है। सामान्य प्रश्न का उत्तर नहीं दिया।
बुह बुह

1
मैं वापस लौटने के लिए इच्छुक हूं Task<string[]>क्योंकि यह इंगित करता है कि आप अब आस्थगित निष्पादन के साथ एक पुनरावृत्तिकर्ता नहीं लौट रहे हैं। सभी URL डाउनलोड किए जा रहे हैं।
जॉननकार्ड्डी

73

tl; D उपज के साथ लागू किए गए I Iterators एक अवरोधक निर्माण हैं, इसलिए अभी इंतजार कर रहे हैं और उपज असंगत हैं।

लंबे समय से क्योंकि एक से अधिक चलने वाला IEnumerableएक अवरोधक ऑपरेशन है, एक विधि को चिह्नित asyncकरना जो अभी भी इसे अवरुद्ध तरीके से निष्पादित करेगा, क्योंकि उसे उस ऑपरेशन को समाप्त करने के लिए इंतजार करना होगा।

async Task<IEnumerable<Foo>> Method(String [] Strs)
{
  foreach(var str in strs)
  {
    yield return await DoSomethingAsync( str)
  }
}  

प्रतीक्षारत Methodमिक्सिंग अर्थ। आप तक इंतजार करना चाहते हैं Taskएक है IEnumerableऔर उसके बाद इस पर पुनरावृत्ति पर ब्लॉक? या आप के प्रत्येक मूल्य का इंतजार करने की कोशिश कर रहे हैं IEnumerable?

मुझे लगता है कि दूसरा वांछित व्यवहार है और उस स्थिति में मौजूदा Iterator शब्दार्थ काम नहीं करेगा। IEnumerator<T>इंटरफ़ेस मूल रूप से है

public interface IEnumerator<T>
  T Current;
  bool MoveNext();
}

मैं अनदेखी कर रहा हूँ Reset()क्योंकि यह अतुल्यकालिक परिणामों के अनुक्रम के लिए कोई मतलब नहीं है। लेकिन आपको जिस चीज की आवश्यकता होगी वह कुछ इस तरह है:

public interface IAsyncEnumerator<T>
  T Current;
  Task<bool> MoveNext();
}

बेशक, foreachयह भी काम नहीं करेगा और आपको इस तरह से मैन्युअल रूप से पुनरावृत्त करना होगा:

var moveNext = await asyncEnumerator.MoveNext();
while(moveNext) {

  // get the value that was fetche asynchronously
  var v = asyncEnumerator.Current;

  // do something with that value

  // suspend current execution context until next value arrives or we are done
  moveNext = await asyncEnumerator.MoveNext();
}

1
क्या आपको लगता है कि यह संभव है कि अगला सी # भाषा संस्करण foreachआपके काल्पनिक के लिए समर्थन जोड़ देगा IAsyncEnumerator<T>?
दाई

1
@ दाई मुझे लगता है कि पाइपलाइन में बहुत कुछ है। C # 8.0 / Had के बारे में पढ़ा था।
नवाफाल


40

C # 8.0 ( लिंक # 1 और लिंक # 2 ) पर नई सुविधाओं के अनुसार, हमारे पास IAsyncEnumerable<T>इंटरफ़ेस समर्थन होगा जो आपके दूसरे प्रयास को लागू करने की अनुमति देगा। यह इस तरह दिखेगा:

async Task<Foo> DoSomethingAsync(string url)
{
    ...
}       
// producing IAsyncEnumerable<T>
async IAsyncEnumerable<Foo> DownLoadAllURL(string[] strs)
{
    foreach (string url in strs)
    {
        yield return await DoSomethingAsync(url);
    }
}
...
// using
await foreach (Foo foo in DownLoadAllURL(new string[] { "url1", "url2" }))
{
    Use(foo);
}

हम C # 5 पर एक ही व्यवहार प्राप्त कर सकते हैं लेकिन एक अलग शब्दार्थ के साथ:

async Task<Foo> DoSomethingAsync(string url)
{
    ...
}
IEnumerable<Task<Foo>> DownLoadAllURL(string[] strs)
{
    foreach (string url in strs)
    {
        yield return DoSomethingAsync(url);
    }
}

// using
foreach (Task<Foo> task in DownLoadAllURL(new string[] { "url1", "url2" }))
{
    Foo foo = await task;
    Use(foo);
}

ब्रायन गिदोन का जवाब है कि कॉलिंग कोड को समानांतर रूप से प्राप्त किए गए परिणामों का एक संग्रह होगा। ऊपर दिए गए कोड का अर्थ है कि कॉलिंग कोड परिणाम की तरह एक-एक करके अतुल्यकालिक तरीके से प्राप्त करेगा।


17

मुझे पता है कि मुझे उत्तर के साथ बहुत देर हो चुकी है, लेकिन यहां एक और सरल समाधान है जिसे इस पुस्तकालय के साथ प्राप्त किया जा सकता है:
GitHub: https://github.com/tyrotoxin/AsyncEnumerable
NuGet.org: https: //www.guget .org / package / AsyncEnumerator /
यह Rx की तुलना में बहुत सरल है।

using System.Collections.Async;

static IAsyncEnumerable<string> ProduceItems(string[] urls)
{
  return new AsyncEnumerable<string>(async yield => {
    foreach (var url in urls) {
      var html = await UrlString.DownLoadHtmlAsync(url);
      await yield.ReturnAsync(html);
    }
  });
}

static async Task ConsumeItemsAsync(string[] urls)
{
  await ProduceItems(urls).ForEachAsync(async html => {
    await Console.Out.WriteLineAsync(html);
  });
}

5

यह सुविधा C # 8.0 के रूप में उपलब्ध होगी। https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/

MSDN से

Async धाराओं

सी # 5.0 की एसिंक्स / प्रतीक्षित सुविधा आपको कॉलबैक के बिना, सीधे कोड में अतुल्यकालिक परिणाम का उपभोग (और उत्पादन) करने देती है:

async Task<int> GetBigResultAsync()
{
    var result = await GetResultAsync();
    if (result > 20) return result; 
    else return -1;
}

यह इतना उपयोगी नहीं है यदि आप परिणामों की निरंतर धाराओं का उपभोग (या उत्पादन) करना चाहते हैं, जैसे कि आप एक IoT डिवाइस या क्लाउड सेवा से प्राप्त कर सकते हैं। उस के लिए Async धाराएँ हैं।

हम IAsyncEnumerable का परिचय देते हैं, जो वास्तव में आप क्या चाहते हैं; IEnumerable का एक अतुल्यकालिक संस्करण। भाषा आपको अपने तत्वों का उपभोग करने के लिए इन पर प्रतीक्षा करने की अनुमति देती है, और तत्वों का उत्पादन करने के लिए उनके पास वापस आती है।

async IAsyncEnumerable<int> GetBigResultsAsync()
{
    await foreach (var result in GetResultsAsync())
    {
        if (result > 20) yield return result; 
    }
}


1

सबसे पहले, ध्यान रखें कि Async सामान समाप्त नहीं हुआ है। C # टीम को अभी भी C # 5 जारी होने से पहले एक लंबा रास्ता तय करना है।

कहा जा रहा है, मुझे लगता है कि आप उन कार्यों को इकट्ठा करना चाह सकते हैं जिन्हें DownloadAllHtmlफ़ंक्शन में अलग तरीके से निकाल दिया जा रहा है ।

उदाहरण के लिए, आप कुछ इस तरह का उपयोग कर सकते हैं:

IEnumerable<Task<string>> DownloadAllUrl(string[] urls)
{
    foreach(var url in urls)
    {
        yield return DownloadHtmlAsync(url);
    }
}

async Task<string> DownloadHtmlAsync(url)
{
    // Do your downloading here...
}

ऐसा नहीं है कि DownloadAllUrlसमारोह है नहीं एक async कॉल। लेकिन, आप किसी अन्य फ़ंक्शन (यानी DownloadHtmlAsync) पर लागू किए गए async कॉल कर सकते हैं ।

टास्क पैरेलल लाइब्रेरी के कार्य .ContinueWhenAnyऔर .ContinueWhenAllकार्य हैं।

इसका उपयोग इस तरह किया जा सकता है:

var tasks = DownloadAllUrl(...);
var tasksArray = tasks.ToArray();
var continuation = Task.Factory.ContinueWhenAll(tasksArray, completedTasks =>
{
    completedtask
});
continuation.RunSynchronously();

यदि आप अपनी विधि को async के रूप में परिभाषित करते हैं, तो आपके पास एक 'हेडर' (लूप से पहले) भी हो सकता है Task<IEnumerable<Task<string>>> DownloadAllUrl। या, यदि आप 'पाद' क्रिया चाहते हैं IEnumerable<Task>। जैसे gist.github.com/1184435
जोनाथन डिकिंसन

1
अब जब कि asyncसामान है समाप्त हो गया और सी # 5.0 है जारी की है, यह कर सकते हैं अद्यतन किया जा।
कैस्परऑन

मुझे यह पसंद है, लेकिन इस मामले में कैसे foreach (var url in wait GetUrls ()) {यीज़ रिटर्न GetContent (url)}; मुझे केवल दो तरीकों में विभाजित करने का एक तरीका मिला।
जुआन पाब्लो गार्सिया कोएलो


-1

यह समाधान उम्मीद के मुताबिक काम करता है। await Task.Run(() => enumerator.MoveNext())भाग पर ध्यान दें ।

using (var enumerator = myEnumerable.GetEnumerator())
{
    while (true)
    {
        if (enumerator.Current != null)
        {
            //TODO: do something with enumerator.Current
        }

        var enumeratorClone = monitorsEnumerator;
        var hasNext = await Task.Run(() => enumeratorClone.MoveNext());
        if (!hasNext)
        {
            break;
        }
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.