title: Task.WaitAll deadlock
body: <p>I have a question regarding Task.WaitAll. At first I tried to use async/await to get something like this:</p>
  
  <pre><code>private async Task ReadImagesAsync(string HTMLtag)
  {
      await Task.Run(() =&gt;
      {
          ReadImages(HTMLtag);
      });
  }
  </code></pre>
  
  <p>Content of this function doesn't matter, it works synchronously and is completely independent from outside world.</p>
  
  <p>I use it like this:</p>
  
  <pre><code>private void Execute()
  {
      string tags = ConfigurationManager.AppSettings["HTMLTags"];
  
      var cursor = Mouse.OverrideCursor;
      Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
      List&lt;Task&gt; tasks = new List&lt;Task&gt;();
      foreach (string tag in tags.Split(';'))
      {
           tasks.Add(ReadImagesAsync(tag));
           //tasks.Add(Task.Run(() =&gt; ReadImages(tag)));
      }
  
      Task.WaitAll(tasks.ToArray());
      Mouse.OverrideCursor = cursor;
  }
  </code></pre>
  
  <p>Unfortunately I get deadlock on <code>Task.WaitAll</code> if I use it that way (with async/await). My functions do their jobs (so they are executed properly), but <code>Task.WaitAll</code> just stays here forever because apparently ReadImagesAsync doesn't return to the caller.</p>
  
  <p>The commented line is approach that actually works correctly. If I comment the <code>tasks.Add(ReadImagesAsync(tag));</code> line and use <code>tasks.Add(Task.Run(() =&gt; ReadImages(tag)));</code> - everything works well.</p>
  
  <p>What am I missing here?</p>
  
  <p>ReadImages method looks like that:</p>
  
  <pre><code>private void ReadImages (string HTMLtag)
  {
      string section = HTMLtag.Split(':')[0];
      string tag = HTMLtag.Split(':')[1];
  
      List&lt;string&gt; UsedAdresses = new List&lt;string&gt;();
      var webClient = new WebClient();
      string page = webClient.DownloadString(Link);
  
      var siteParsed = Link.Split('/');
  
      string site = $"{siteParsed[0]} + // + {siteParsed[1]} + {siteParsed[2]}";
  
      int.TryParse(MinHeight, out int minHeight);
      int.TryParse(MinWidth, out int minWidth);
  
      int index = 0;
  
      while (index &lt; page.Length)
      {
          int startSection = page.IndexOf("&lt;" + section, index);
          if (startSection &lt; 0)
              break;
  
          int endSection = page.IndexOf("&gt;", startSection) + 1;
          index = endSection;
  
          string imgSection = page.Substring(startSection, endSection - startSection);
  
          int imgLinkStart = imgSection.IndexOf(tag + "=\"") + tag.Length + 2;
          if (imgLinkStart &lt; 0 || imgLinkStart &gt; imgSection.Length)
              continue;
  
          int imgLinkEnd = imgSection.IndexOf("\"", imgLinkStart);
          if (imgLinkEnd &lt; 0)
              continue;
  
          string imgAdress = imgSection.Substring(imgLinkStart, imgLinkEnd - imgLinkStart);
  
          string format = null;
          foreach (var imgFormat in ConfigurationManager.AppSettings["ImgFormats"].Split(';'))
          {
              if (imgAdress.IndexOf(imgFormat) &gt; 0)
              {
                  format = imgFormat;
                  break;
              }
          }
  
          // not an image
          if (format == null)
              continue;
  
          // some internal resource, but we can try to get it anyways
          if (!imgAdress.StartsWith("http"))
              imgAdress = site + imgAdress;
  
          string imgName = imgAdress.Split('/').Last();
  
          if (!UsedAdresses.Contains(imgAdress))
          {
              try
              {
                  Bitmap pic = new Bitmap(webClient.OpenRead(imgAdress));
                  if (pic.Width &gt; minHeight &amp;&amp; pic.Height &gt; minWidth)
                      webClient.DownloadFile(imgAdress, SaveAdress + "\\" + imgName);
              }
              catch { }
              finally
              {
                  UsedAdresses.Add(imgAdress);
              }
          }
  
      }
  }
  </code></pre>