-2

I'm new to this area of C# and, frankly, struggling to grok the paradigm. It seems I'm not alone (Where does async and await end? Confusion, http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html)

In my case I am writing a small TCP server in a C# library, in essence the TCP server should run in its own thread and post data back to the application via a provided callback. So we might have an entrypoint into the library:

class MyServer
{
 void StartServerRunningAsync(Callback callback)
 {
  this.callback = callback; //calls back into unmanaged via 'magic' COM interop each time a TCP client posts interesting data
  StartRunningThread(); //this creates a thread to run the server and returns 
 }
 void StartRunningThread()
 {
  new Thread(new ThreadStart(Run)).Start();
 }
 void Run()
 {
  //do standard TCP async stuff treating `Run` like a local `Main`
 }
}

This library will be used from an unmanaged C++ application (specifically via COM in this case) which runs the server in the background. So I don't think StartServerRunning can/should be async but then that means I'm stuck/confused how I can use async/await at all since it propagates through your whole stack based on the links above.

Is this actually an issue or have I misunderstood something fundamental? How can TPL be encapsulated in this way?

Mr. Boy
  • 60,845
  • 93
  • 320
  • 589
  • Is `StartRunningThreadAsync` async as the name suggest? Why? Does it not start a thread as the comment says? Perhaps this helps: [Why is it necessary for every new api to be async?](https://softwareengineering.stackexchange.com/q/396107/45814). – Theraot Jan 29 '20 at 11:23
  • @Theraot I anticipate it runs up a new thread which is basically a `while(dontQuit)` loop. I don't know if it's `async` that's kind of the point... it seems if I use TPL then it _must_ be? Hence my confusion. – Mr. Boy Jan 29 '20 at 11:26
  • @Theraot it still seems like there must be a way to 'encapsulate' TPL usage. What if I have an existing codebase that is pre-TPL and I want to plug in some 3rd party library. It feels like I should be able to explicitly run up a separate dedicated thread that then runs that library a bit like my pseudo code above. I'd be interested in an answer expressing this approach if it is feasible. – Mr. Boy Jan 29 '20 at 11:48

1 Answers1

0

Actually, using async/await makes things much easier.

class MyServer
{
 // notice async void here instead of async Task
 async void StartServerRunning(Callback callback)
  {
   await StartRunningThreadAsync(); // start server asynchronously
   callback();
  }
}

From external observer's view, StartServerRunning exits immediately as it encounters first await. But in the background, the method is still "running". And once StartRunningThreadAsync finishes, the callback is then called. This is not be good practice when used inside C# code, but I don't see it as a bad thing when C interop is involved.

I would also recommend to implement exception handling so that no exception can get outside this method. Like :

class MyServer
{
 async void StartServerRunning(Callback callback)
  {
   bool wasError = false;
   try{
     await StartRunningThreadAsync(); // start server asynchronously
   }catch(Exception ex)
   {
     // log exception
     wasError = true;
   }
   callback(wasError);
  }
}
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Euphoric
  • 12,645
  • 1
  • 30
  • 44
  • I don't know it makes much difference to your answer but `callback` is called repeatedly during the life of the thread, not when work is finished (well it might be both!). I have absolutely no idea what happens calling an `async` method from outside .Net, you think that would work? – Mr. Boy Jan 29 '20 at 11:33
  • 2
    @Mr.Boy `async void` as far as the rest of the world is concerned is just `void`. In fact, `async` does not exist in IL. Thus, calling it should be the same as calling it if it were just `void`. – Theraot Jan 29 '20 at 11:34
  • If I understand correctly then, I would not _need_ to explicitly create a dedicated thread at all now. Just use TPL throughout and CLR will ensure threads exist as needed. I updated my question with what I _thought_ would happen if I don't use your approach – Mr. Boy Jan 29 '20 at 11:39
  • 1
    @Mr.Boy That is how it works. One more thing I would worry is what thread the callback is called on. I don't have experience with C interop to tell you exactly what are possible ramifications of running C callback on a CLR thread. So you will need to experiment a bit with that. But that is same if you use plain threads or TPL. – Euphoric Jan 29 '20 at 11:41
  • @Euphoric yeah it's a bit out of scope but IIRC COM actually marshalls all this across threads anyway. When I call a C# method directly from unmanaged C++ over COM it actually runs the C# in a different thread for safety reasons. – Mr. Boy Jan 29 '20 at 11:44