0

I have these methods in a class

    public async Task GetCompanies(int requestDuration, long startTimepoint)
    {
        _requestDuration = requestDuration;
        _startTimepoint = startTimepoint;

        Thread thread = new Thread(new ThreadStart(Test));

        // This line doesnt compile - No overload for GetCompaniesApi matches delegate ThreadStart
        Thread thread2 = new Thread(new ThreadStart(GetCompaniesApi));
    }

    public void Test()
    {
    }

    public async Task GetCompaniesApi (int requestDuration, long? startTimepoint)
    {
        // code removed as not relevant
    }

So my question is how can I run a method that is async in a different thread, I don't really know what "No overload for GetCompaniesApi matches delegate ThreadStart" means, or what I need to change.

EDIT

If I just explain fully what i'm trying to do that might be better than the more specific question I asked at the start.

Basically I want to call a HTTP GET request which is streaming, as in it never ends, so I want to force the HTTP GET request to end after X seconds and whatever we have got from the response body at that point will be it.

So in order to try and do this I thought i'd run that HTTP GET request in a separate thread, then sleep the main thread, then somehow get the other thread to stop. I don't see how its possible to use cancellation tokens as the thread is stuck on line "await _streamToReadFrom.CopyToAsync(streamToWriteTo);" all the time.

    public async Task GetCompanies(int requestDuration, long startTimepoint)
    {
        Task task = Task.Run(() => { GetCompaniesApi(requestDuration, startTimepoint); });

        Thread.Sleep(requestDuration * 1000);

        // Is it now possible to cancel task?
    }


    public async Task GetCompaniesApi (int requestDuration, long? startTimepoint)
    {
        string url = $"https://stream.companieshouse.gov.uk/companies?timepoint={startTimepoint}";

        using (HttpResponseMessage response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
        using (_streamToReadFrom = await response.Content.ReadAsStreamAsync())
        {
            string fileToWriteTo = Path.GetTempFileName();
            using (Stream streamToWriteTo = System.IO.File.Open(fileToWriteTo, FileMode.Create))
            {
                await _streamToReadFrom.CopyToAsync(streamToWriteTo);
            }
        }
    }
Paul
  • 856
  • 1
  • 8
  • 18
  • 4
    Is there any particular reason to manage threads directly? – Guru Stron Feb 15 '23 at 15:24
  • 1
    *"How can I run a method that is async in a different thread?"* -- I would suggest to read this article by Stephen Cleary: [There is no thread](https://blog.stephencleary.com/2013/11/there-is-no-thread.html). Then you'll be able to express your question in better terms. – Theodor Zoulias Feb 15 '23 at 15:54
  • 1
    Related: [.NET Core equivalent to Thread.Abort](https://stackoverflow.com/questions/53465551/net-core-equivalent-to-thread-abort), and [How to abort a Task like aborting a Thread?](https://stackoverflow.com/questions/4359910/how-to-abort-a-task-like-aborting-a-thread-thread-abort-method) – Theodor Zoulias Feb 15 '23 at 16:20
  • 1
    Thanks, i've got something working, I just closed the stream that the child thread was using in the main thread and it's doing what I want, so I don't need any more help – Paul Feb 15 '23 at 16:29

2 Answers2

4

ThreadStart is a delegate representing parameterless method with void return value (so it it not async aware, i.e. the tread will not wait for task completion), while your method requires 2 parameters to be passed so from purely technical standpoint you can do something like new ThreadStart(() => GetCompaniesApi(1,2)), or just new Thread(() => GetCompaniesApi(1, 2)) (compiler will create the delegate for you). But you should not.

In modern .NET rarely there is need to create threads directly, just use Task.Run to schedule your async method on the thread pool (do not forget to provide the parameters):

await Task.Run(() => Test());

For async method - just invoke it:

var result = await GetCompaniesApi(someInt, someNullableLong);

If for some reason it is not actually async then better fix the method itself, but if needed you can wrap it into Task.Run too.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
1

I think the question reveals a potential misunderstanding of multi threading and async.

The main purpose of async is to hide the latency of IO operations, like network or disk access. That helps reducing resource usage, and keeping the UI responsive.

Using background threads are mostly for hiding the latency of computations. For example if you are doing some slow image processing task. In that case you typically use Task.Run to execute a synchronous method, and await the result. Just make sure the method is thread safe.

While there are cases that mix these types of work and where you need to combine both methods, it is not that common. From your code I would guess your work is mostly IO related, so you should probably not use any backgrounds threads. Note that some libraries lie about being asynchronous, i.e. methods that return a task, but will still block. In that treating the method as a synchronous thread and use Task.Run to execute it might be warranted.

Also keep in mind that multi threaded programming is difficult. It introduces a great number of new types of faults, and most faults will not be caught by the compiler, and many are spurious and difficult to reproduce. So you really need to be aware of the dangers and know how to correctly use locks and other forms of synchronization.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • Thanks. GetCompaniesApi method does a streaming HTTP GET, a request that never ends unless I do something to end it, so i'm running in in a different thread so that I can sleep the main thread for x seconds then cancel the separate thread. It needs to be async because i'm using other async methods within it. – Paul Feb 15 '23 at 15:54
  • @Paul that is probably a good case for *not* running the method on a background thread. Since the async call will not block the thread, there is no real need for calling it on a separate thread. But the exact solution will depend a bit what kind of application you are writing. Cancelling can be done with events or a timer, a loop around sleep is rarely the best solution. – JonasH Feb 16 '23 at 07:37