2

I have a button that starts two threads

private void CrawdBtn_Click(object sender, EventArgs e)
{
    CrawdBtn.Enabled = false;
    t = new Thread(AddLinksToList);
    b = new Thread(EnqueueFromList);
    t.Start();
    b.Start();            
}

and there are another buttons to pause, Resume, Stop those threads My question is how can I disable (pause, Resume, Stop) buttons while the threads are working and re enable Crawl after the threads finished

selfstudy
  • 523
  • 2
  • 15
Mead Alsorani
  • 41
  • 1
  • 12
  • 1
    This would be relatively easy to do using `async/await`, are you opposed to doing it that way? – Ron Beyer Dec 31 '19 at 16:53
  • 1
    https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=netframework-4.8 – GSerg Dec 31 '19 at 16:53
  • 1
    Read [this](https://stackoverflow.com/questions/36266149/pause-and-resume-a-thread-from-another-thread-in-c-sharp) carefully. –  Dec 31 '19 at 16:54
  • 1
    The `Thread` class does not have an event `Completed` or `Finished` or `Exited`, so you must include checking code inside both methods `AddLinksToList` and `EnqueueFromList`. It would be much easier and cleaner if you used `Task`s instead of threads. – Theodor Zoulias Dec 31 '19 at 16:54
  • In fact I have to use thread because I don't have a lot of information about tasks and there is no time to learn tasks from zero – Mead Alsorani Dec 31 '19 at 16:57
  • 1
    Then the GSerg's link is your friend for now. –  Dec 31 '19 at 16:58
  • Do you really use the methods [`Thread.Suspend`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.suspend) and [`Thread.Resume`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.resume)? – Theodor Zoulias Dec 31 '19 at 17:04
  • @TheodorZoulias Yes I use them to pause and resume threads – Mead Alsorani Dec 31 '19 at 17:06
  • 1
    Sincerely, this stuff is obsolete. It may be time to learn about newer techniques. The time will be well spent. – Theodor Zoulias Dec 31 '19 at 17:51

2 Answers2

1

Here is how you could start a Thread and have a way to await its completion:

public static Thread CreateAwaitableThread(Action action, out Task threadCompletion)
{
    var tcs = new TaskCompletionSource<bool>();
    threadCompletion = tcs.Task;
    return new Thread(() =>
    {
        try
        {
            action();
        }
        finally
        {
            tcs.SetResult(true);
        }
    });
}

This method returns the newly created Thread, and also a Task that will be completed when the Thread is completed. You could use it like this:

private async void CrawdBtn_Click(object sender, EventArgs e)
{
    CrawdBtn.Enabled = false;
    Thread t1 = CreateAwaitableThread(AddLinksToList, out var t1c);
    Thread t2 = CreateAwaitableThread(EnqueueFromList, out var t2c);
    t1.Start();
    t2.Start();
    await Task.WhenAll(t1c, t2c);
    CrawdBtn.Enabled = true;
}

In case of an exception the error will not be propagated through the Task. It is assumed that the delegates already include error handling logic. If not, an unhandled exception will occur as usual.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
1

To solve your problem you can make a thread to check the ThreadState of thread t and thread b

private void btnstart_Click(object sender, EventArgs e)
{
  
    t = new Thread(AddLinksToList);
    b = new Thread(EnqueueFromList);


    t.Start();
    b.Start();
    if (threadchecker == null)//this if determines Whether it's the first time or not
    {
        threadchecker = new Thread(() => ChekingStateOfThreads());
        threadchecker.IsBackground = true;
        threadchecker.Start();
    }
}

Since the thread wants to check the ThreadState of those threads it should always run. and This is ChekingStateOfThreads

private void ChekingStateOfThreads()
{
    while (true)
    {
        Thread.Sleep(1000);
   
        if (t.ThreadState == ThreadState.Stopped)
        {
            this.Invoke(new Action(() =>
            {
                btnpause.Enabled = btnstart.Enabled = false;
                btnresume.Enabled = btnstop.Enabled = true;
            }));
        }
        else if (t.ThreadState == ThreadState.Running)
        {
            this.Invoke(new Action(() =>
            {
                btnstart.Enabled = btnresume.Enabled = false;
                btnpause.Enabled = btnstop.Enabled = true;
            }));
        }
        else if (t.ThreadState == ThreadState.Aborted)
        {
            this.Invoke(new Action(() =>
            {
                btnstart.Enabled = true;
                btnpause.Enabled = btnresume.Enabled = btnstop.Enabled = false;
            }));
        }
        else if (t.ThreadState == ThreadState.Suspended)
        {
            this.Invoke(new Action(() =>
            {
                btnpause.Enabled = btnstart.Enabled = false;
                btnresume.Enabled = btnstop.Enabled = true;
            }));
        }
    }
}

The concept of function is pretty simple. Each 1 second the thread is check the state of thread t.

See Why use Invoke on Controls in .net? to figure out why should we use INVOKE.

To abort the threadchecker just use the Form_closing Event

private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
    threadchecker.Abort();
}
ad79h
  • 53
  • 4