2013-10-04 7 views
8

संपादितएसिंक्रोनस रूप से और समांतर फ़ाइलें डाउनलोड

मैं प्रश्न के शीर्षक मैं था मुद्दा लेकिन यह भी कितनी आसानी से इस लक्ष्य को हासिल करने के लिए पर एक जवाब प्रतिबिंबित करने के लिए बदल दिया है।


मैं Task<TResult> बजाय Task 1 विधि में के रूप में वापस जाने के लिए 2 विधि बनाने के लिए कोशिश कर रहा हूँ लेकिन मैं इसे ठीक करने की कोशिश का एक परिणाम के रूप में त्रुटियों की एक झरना मिल रहा है।

  • मैं await body(partition.Current);
  • से पहले return जोड़ा बारी में यह मुझे पूछता है तो मैं नीचे
  • return null जोड़ा नीचे एक वापसी कथन को जोड़ने के लिए लेकिन अब चुनिंदा बयान शिकायत है कि यह क्वेरी
  • से प्रकार तर्क अनुमान नहीं लगा सकता
  • मैं Task.Run से Task.Run<TResult> बदलता हूं लेकिन सफलता के बिना।

मैं इसे कैसे ठीक कर सकता हूं?

पहली विधि http://blogs.msdn.com/b/pfxteam/archive/2012/03/05/10278165.aspx से आता है, दूसरी विधि अधिभार है जिसे मैं बनाने की कोशिश कर रहा हूं।

public static class Extensions 
{ 
    public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body) 
    { 
     return Task.WhenAll(
      from partition in Partitioner.Create(source).GetPartitions(dop) 
      select Task.Run(async delegate 
      { 
       using (partition) 
        while (partition.MoveNext()) 
         await body(partition.Current); 
      })); 
    } 

    public static Task ForEachAsync<T, TResult>(this IEnumerable<T> source, int dop, Func<T, Task<TResult>> body) 
    { 
     return Task.WhenAll(
      from partition in Partitioner.Create(source).GetPartitions(dop) 
      select Task.Run(async delegate 
      { 
       using (partition) 
        while (partition.MoveNext()) 
         await body(partition.Current); 
      })); 
    } 
} 

प्रयोग उदाहरण:

इस पद्धति से

मैं समानांतर और एसिंक्रोनस रूप में अनेक फ़ाइलों को डाउनलोड करना चाहते हैं:

private async void MainWindow_Loaded(object sender, RoutedEventArgs e) 
{ 
    Artist artist = await GetArtist(); 
    IEnumerable<string> enumerable = artist.Reviews.Select(s => s.ImageUrl); 
    string[] downloadFile = await DownloadFiles(enumerable); 
} 

public static async Task<string[]> DownloadFiles(IEnumerable<string> enumerable) 
{ 
    if (enumerable == null) throw new ArgumentNullException("enumerable"); 
    await enumerable.ForEachAsync(5, s => DownloadFile(s)); 
    // Incomplete, the above statement is void and can't be returned 
} 

public static async Task<string> DownloadFile(string address) 
{ 
    /* Download a file from specified address, 
     * return destination file name on success or null on failure */ 

    if (address == null) 
    { 
     return null; 
    } 

    Uri result; 
    if (!Uri.TryCreate(address, UriKind.Absolute, out result)) 
    { 
     Debug.WriteLine(string.Format("Couldn't create URI from specified address: {0}", address)); 
     return null; 
    } 

    try 
    { 
     using (var client = new WebClient()) 
     { 
      string fileName = Path.GetTempFileName(); 
      await client.DownloadFileTaskAsync(address, fileName); 
      Debug.WriteLine(string.Format("Downloaded file saved to: {0} ({1})", fileName, address)); 
      return fileName; 
     } 
    } 
    catch (WebException webException) 
    { 
     Debug.WriteLine(string.Format("Couldn't download file from specified address: {0}", webException.Message)); 
     return null; 
    } 
} 
+1

यह स्पष्ट नहीं है कि आप परिणाम की अपेक्षा करेंगे। आप 'T' मूल्यों की एक पूरी अनुक्रम में गुजर रहे हैं, और उन दोनों पर एक ही फ़ंक्शन को निष्पादित - क्या एकल परिणाम आप की' टास्क 'लौटे बाहर निकलने के लिए उम्मीद करेंगे? –

+0

मुझे लगता है कि मामले में एक टास्क प्राप्त करना चाहते हैं, मैं अपने प्रश्न पर एक उदाहरण जोड़ दिया है। – Aybe

+0

* * "इस विधि के साथ मैं समानांतर में एक से अधिक फ़ाइलों को डाउनलोड करने और अतुल्यकालिक रूप से करना चाहते हैं": 'Parallel.Foreach' पर्याप्त नहीं है? –

उत्तर

23

मैं इसे और इसे यहाँ पोस्ट कर हल, किसी को भी हो रही मदद कर सकता है वही मुद्दा।

मेरे प्रारंभिक जरूरत एक छोटे से सहायक है कि जल्दी से छवियों डाउनलोड होगा, लेकिन यह भी सिर्फ कनेक्शन ड्रॉप यदि सर्वर जल्दी से जवाब नहीं है, समानांतर में यह सब और अतुल्यकालिक रूप से था।

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

  • आप एक नहीं है, तो के लिए आप
  • वैकल्पिक रूप से एक डाउनलोड रद्द करने के लिए एक अवधि (आसान बना दिया जाएगा,
  • आप जहां यह सहेजा जाएगा एक स्थानीय फ़ाइल नाम निर्दिष्ट कर सकते हैं डाउनलोड करने के लिए यूआरएल की एक सूची निर्दिष्ट धीमी या नहीं पहुंचा जा सकता सर्वर के लिए)

तुम बस DownloadFileTaskAsync ही उपयोग करें या समानांतर और अतुल्यकालिक डाउनलोड के लिए ForEachAsync सहायक का उपयोग कर सकते हैं। यह कैसे उपयोग करने पर एक उदाहरण के साथ

कोड:

private async void MainWindow_Loaded(object sender, RoutedEventArgs e) 
{ 
    IEnumerable<string> enumerable = your urls here; 
    var results = new List<Tuple<string, string, Exception>>(); 
    await enumerable.ForEachAsync(s => DownloadFileTaskAsync(s, null, 1000), (url, t) => results.Add(t)); 
} 

/// <summary> 
///  Downloads a file from a specified Internet address. 
/// </summary> 
/// <param name="remotePath">Internet address of the file to download.</param> 
/// <param name="localPath"> 
///  Local file name where to store the content of the download, if null a temporary file name will 
///  be generated. 
/// </param> 
/// <param name="timeOut">Duration in miliseconds before cancelling the operation.</param> 
/// <returns>A tuple containing the remote path, the local path and an exception if one occurred.</returns> 
private static async Task<Tuple<string, string, Exception>> DownloadFileTaskAsync(string remotePath, 
    string localPath = null, int timeOut = 3000) 
{ 
    try 
    { 
     if (remotePath == null) 
     { 
      Debug.WriteLine("DownloadFileTaskAsync (null remote path): skipping"); 
      throw new ArgumentNullException("remotePath"); 
     } 

     if (localPath == null) 
     { 
      Debug.WriteLine(
       string.Format(
        "DownloadFileTaskAsync (null local path): generating a temporary file name for {0}", 
        remotePath)); 
      localPath = Path.GetTempFileName(); 
     } 

     using (var client = new WebClient()) 
     { 
      TimerCallback timerCallback = c => 
      { 
       var webClient = (WebClient) c; 
       if (!webClient.IsBusy) return; 
       webClient.CancelAsync(); 
       Debug.WriteLine(string.Format("DownloadFileTaskAsync (time out due): {0}", remotePath)); 
      }; 
      using (var timer = new Timer(timerCallback, client, timeOut, Timeout.Infinite)) 
      { 
       await client.DownloadFileTaskAsync(remotePath, localPath); 
      } 
      Debug.WriteLine(string.Format("DownloadFileTaskAsync (downloaded): {0}", remotePath)); 
      return new Tuple<string, string, Exception>(remotePath, localPath, null); 
     } 
    } 
    catch (Exception ex) 
    { 
     return new Tuple<string, string, Exception>(remotePath, null, ex); 
    } 
} 

public static class Extensions 
{ 
    public static Task ForEachAsync<TSource, TResult>(
     this IEnumerable<TSource> source, 
     Func<TSource, Task<TResult>> taskSelector, Action<TSource, TResult> resultProcessor) 
    { 
     var oneAtATime = new SemaphoreSlim(5, 10); 
     return Task.WhenAll(
      from item in source 
      select ProcessAsync(item, taskSelector, resultProcessor, oneAtATime)); 
    } 

    private static async Task ProcessAsync<TSource, TResult>(
     TSource item, 
     Func<TSource, Task<TResult>> taskSelector, Action<TSource, TResult> resultProcessor, 
     SemaphoreSlim oneAtATime) 
    { 
     TResult result = await taskSelector(item); 
     await oneAtATime.WaitAsync(); 
     try 
     { 
      resultProcessor(item, result); 
     } 
     finally 
     { 
      oneAtATime.Release(); 
     } 
    } 
} 

मैं ForEachAsync के हस्ताक्षर समानांतरवाद का स्तर चुनने के नहीं बदला है, मैं तुम्हें इसे समायोजित के रूप में आप चाहते हैं दूँगा।

आउटपुट उदाहरण:

:

DownloadFileTaskAsync (null local path): generating a temporary file name for http://cache.thephoenix.com/secure/uploadedImages/The_Phoenix/Music/CD_Review/main_OTR_Britney480.jpg 
DownloadFileTaskAsync (null local path): generating a temporary file name for http://ssimg.soundspike.com/artists/britneyspears_femmefatale_cd.jpg 
DownloadFileTaskAsync (null local path): generating a temporary file name for http://a323.yahoofs.com/ymg/albumreviewsuk__1/albumreviewsuk-526650850-1301400550.jpg?ymm_1xEDE5bu0tMi 
DownloadFileTaskAsync (null remote path): skipping 
DownloadFileTaskAsync (time out due): http://hangout.altsounds.com/geek/gars/images/3/9/8/5/2375.jpg 
DownloadFileTaskAsync (time out due): http://www.beat.com.au/sites/default/files/imagecache/630_315sr/images/article/header/2011/april/britney-spears-femme-fatale.jpg 
DownloadFileTaskAsync (time out due): http://cache.thephoenix.com/secure/uploadedImages/The_Phoenix/Music/CD_Review/main_OTR_Britney480.jpg 
DownloadFileTaskAsync (downloaded): http://newblog.thecmuwebsite.com/wp-content/uploads/2009/12/britneyspears1.jpg 
DownloadFileTaskAsync (downloaded): http://newblog.thecmuwebsite.com/wp-content/uploads/2009/12/britneyspears1.jpg 
DownloadFileTaskAsync (downloaded): http://static.guim.co.uk/sys-images/Music/Pix/site_furniture/2011/3/22/1300816812640/Femme-Fatale.jpg 
DownloadFileTaskAsync (downloaded): http://www.sputnikmusic.com/images/albums/72328.jpg 

क्या 1 मिनट तक का समय लग करने के लिए इस्तेमाल अब मुश्किल से इन 2 पदों के लेखक को

और बड़ा धन्यवाद एक ही परिणाम :) के लिए 10 सेकंड लेता है

http://blogs.msdn.com/b/pfxteam/archive/2012/03/05/10278165.aspx

http://blogs.msdn.com/b/pfxteam/archive/2012/03/04/10277325.aspx

+1

ग्रेट एक्सटेंशन विधियां! – nullable

+0

हां वे बहुत उपयोगी हैं, धन्यवाद! – Aybe

+1

अच्छा काम महोदय :) –

संबंधित मुद्दे