2

OK so I'm working on a little MP3 player app. It uses a DataSet to hold MP3 file info. This question is about how to load my DataSet asynchronously. I have been reading around quite a bit, and tried a bunch of stuff, but I just don't get it. All I want to do is take a long-running process DataSet fill process and make it asynchronous.

In The Beginning, I had a method of my DataSet (LoadMusic) that looked like this:

public partial class dsMusicPlayer
{
    public void LoadMusic()
    { ... }
}

It scans the MP3 files in a directory structure and fills the DataSet with the file info (title, artist, album, etc). It works, but takes a minute or so to run, and blocks the UI naturally. So, perfect candidate for being made asynchronous. I re-wrote my load method like this (without changing implementation otherwise):

public async void LoadMusicAsync()
{
}

I knew it wouldn't be that simple, but I'm going one step at a time. Sure enough, I get a warning that my async method 'lacks await operators.' All the reading I've done shows "await" being used before a method call, so I refactor the body of my original method out to a dummy private method whose only purpose is to allow the 'await' keyword to be used in the container/public method:

public async void LoadMusicAsync()
{
    await LoadMusicInner();
}

private void LoadMusicInner()
{
}

Next I see that I can't 'await void.' Fine. I change my private method to

private Task LoadMusicInner()
{
}

But now I get the error 'not all code paths return a value' -- it makes sense because the return type is Task. And here's the point where I feel stuck. My method was void to begin with because it really doesn't return anything. How should I have done this?

3
  • btw I eventually gave up an used an old-school BackgroundWorker object that I understand, but my question still stands.
    – user2023653
    Commented Dec 12, 2013 at 16:01
  • 3
    There are numerous articles on async/await basics. Commented Dec 12, 2013 at 16:11
  • I read a handful, but couldn't really get anywhere.
    – user2023653
    Commented Dec 12, 2013 at 19:40

2 Answers 2

5

It should look more like this:

public async Task LoadMusicAsync()
{
    await Task.Run(() => this.LoadMusicInner());
}

private void LoadMusicInner()
{
    // Work here..
}
3
  • thanks, haven't had a chance to try yet, but will when I get home.
    – user2023653
    Commented Dec 12, 2013 at 19:41
  • 2
    Most of the time, it shouldn't look like this. Library methods shouldn't use Task.Run() just to pretend to be asynchronous.
    – svick
    Commented Dec 12, 2013 at 19:44
  • @svick async tasks of determinable length are acceptable candidates for Task.Run. Starting your own thread is the ultimate best practice but a custom scheduler (that avoids thread pooled threads) will provide the same results. The slimmest solution is TaskCreationOptions.LongRunning` - it causes the default scheduler to avoid the threadpool when set. msdn.microsoft.com/en-us/library/…
    – Gusdor
    Commented May 28, 2014 at 7:09
2

If all you want to do is to execute some synchronous code on a background thread, then in your calling code, you can do something like:

await Task.Run(() => player.LoadMusic());

This requires you to make the whole method where you're making this call async. If it's not a top-level event handler, then it should be an async Task method and its caller should await it too, spreading async all the way to the top. The top-level event handler then should be async void (avoid async void pretty much everywhere else).

If you want to make your code truly asynchronous (i.e. not blocking a thread when doing IO), then you need to actually change the implementation of LoadMusic() by replacing synchronous calls (like reader.ReadLine()) into their asynchronous versions (like await reader.ReadLineAsync()).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.