0

I'm trying to make a series of microservices and am currently working on a base class for them utilizing a .Net Core console app. The goal is to have the services, deployed as Docker containers, be able to monitor a source of information (Kafka, SQL, whatever) ,and then activate when they find their trigger. A dev should be able to just grab this base class and put their logic in place without having to worry about the logic triggering it or any other underlying service logic.

To do this I'm trying to utilize threading in the base class in a way that the user of the class doesn't have to deal with it but keeps the service responsive and able to monitor/respond to external commands (like the ability to get status, cancel a job, etc). Unfortunately it seems that Microsoft is trying to do away with System.Threading in .Net core, since I can start threads them but I can't stop them. This removes the ability to cancel/pause a job, which in turn removes the ability to shut down the service since I cannot stop the thread.

I've looked at Tasks, but they seem to be for web development. In fact most if not all information I've found about threading and microservices in .Net Core refers to making WebAPIs in ASP .Net. Other than that I really haven't found much information about how to use System.Threading now that every control other than .Start seems to have been removed.

I'm about to look at a system where I have the thread host a Task, then try using Task controls to see if I can cancel it and then have the thread just gracefully drop (similar to answer from here: dotnet core equivalent to Thread.Abort). This doesn't feel right, like I'm making a toothpick tower that can easily break, however I need to have the base service maintain control over the thread; I don't want myself or others to have to continuously check a token throughout their logic to see if the thread is supposed to be shut down.

Is there a better way of going about what I'm trying to do here? I've been looking for days but I keep running into the same stuff and it's all about how to make webapis utilizing Tasks, which is nothing like what I'm trying to do here; they pause the main logic rather than run independently. Guess it's an annoying bi-product of this "everything must be a website" frame of mind in the industry. Not sure if I'm missing something obvious by asking the wrong questions or if it's just going to be this odd of a job.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Dropn Fbombs
  • 119
  • 3
  • 15
  • 3
    Task is a universal abstraction for a thread, it is not just useful in web apps. – Hans Passant Feb 17 '20 at 19:25
  • Is there a way to set a task to run in the background and not disturb the flow of the main thread? I've not found how to do that yet, though I'm just getting started on using tasks. So far my code just stops and runs the task until completion, and it appears that async requires await to work, which means that my main thread would just be sitting at the await call. It doesn't appear to be independent like a thread is. – Dropn Fbombs Feb 17 '20 at 19:40
  • Using Thread.Abort is about as far from "graceful" as you can possibly get. It's the equivalent of taking the thread out into the forest and shooting it. There is a reason the task system is based on cooperative threading, it's the only safe route to go. – Lasse V. Karlsen Feb 17 '20 at 19:40
  • The `Task` infrastructure will save you the tedious (and potentially dangerous) task of handling the whole lifecycle of your threads. You apparently "just" want your application to be responsive, that's exactly what await / async + Task syntax is for. Did you actually try to implement it that way ? Did you experiment some measurable poor performance on a specific setup ? – Pac0 Feb 17 '20 at 19:40
  • To construct a "background task", just use `Task.Run`, or if the bulk of the code is actually asynchronous operations, just use normal `async/await` semantics to build your code. – Lasse V. Karlsen Feb 17 '20 at 19:40
  • 1
    "Is there a way to set a task to run in the background and not disturb the flow of the main thread" -> just run the Task without await, if it's "hot". Id it's cold, you can use [`Task.Start`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.start?view=netframework-4.8) (or Task.Run to create it as well) – Pac0 Feb 17 '20 at 19:41
  • 1
    Awesome, thanks Pac0, that worked. I don't know what I was doing differently that caused it to not work before. Previous attempts at creating a task variable and setting it to run just paused the main thread in place, waiting on the task to end, but this time using Task.Start worked. If you'd like to post this as the answer I'd be happy to accept it. – Dropn Fbombs Feb 17 '20 at 19:50

1 Answers1

2

Task and await/async language features are a nice way to abstract threading, and are supposed to help exactly for what you wish for (keep the application responsive by using thread pool), whilst saving you the hassle of properly create and manage all those threads (not to mention catching exceptions during asynchronous execution).

As stated in comments, I think you should give it a try "properly" and then measure if it meets your performance objectives.

Some of the functions returning a Task return them "hot" (meaning, already executing their work). If you do not wish to "pause the Main flow", just do not await the Task returned until the end of your workflow. By the way, this should be the case when using Task.Run.

Sometimes, Task are "cold", and started automatically when await is called. But you can start them manually without awaiting them by using Task.Start.

Of course, using Tasks have it's pitfalls and you should should still be aware of what it does behind the scenes. You can still do "bad" thread programming things, for instance cause deadlocks in some situations, or fail to handle exceptions.

Further reading, picked up among many :

but there are many more.

Pac0
  • 21,465
  • 8
  • 65
  • 74
  • Thank you for your answer. It seems like the simple solution and I don't know what I was doing so differently that cause the program to sit and wait for the task to finish rather than just run it as a separate thread as it is doing now, nor why it was so difficult to find this answer that I needed to ask here. I still have more research to do so I can figure out how to properly deal with parameters and Task control, but this gets me on the right foot. Exiting the program now works which was the biggest issue with the use of Thread. I'll look at the link you provided as well, thank again. – Dropn Fbombs Feb 17 '20 at 20:21
  • Yes, using Tasks has its learning curve, hope this will take you one the right track. If you have further question feel free to post another question on Stack Overflow (of course by doing research first), along with some [mcve], that always helps questions being more focused (and better received by the community) – Pac0 Feb 17 '20 at 20:31
  • I mean that's the thing, I didn't just come here as soon as I was stumped, I wrote a basic console app to test various ways of running tasks to see if they could be used this way. I looked them up and every time I'd end up at some ASP .Net deal from Microsoft talking about using async and await for webapis. Even doing base tests where I'd just start the task didn't seem to work until this time. Anyway I'll try to remember to be more brief next time, just wasn't sure if I was missing something in .Net Core for microservices that I should be using (searching brings up webapi stuff). – Dropn Fbombs Feb 17 '20 at 20:46