0

I have considerable programming knowledge but this is the first time I'm working on a multi-threaded application on C#, so I'm asking for help regarding my problem.

Firstly, the codes,

public frmCEX()
    {
        InitializeComponent();
        refreshTicker();
    }

    private void btnRefresh_Click(object sender, EventArgs e)
    {
        refreshTicker();
    }

    private void refreshTicker()
    {
        ssStatus.Text = "Updating ticker..";
        btnRefresh.Text = "Updating";
        btnRefresh.Enabled = false;
        ssUpdated.Text = "Last updated: -";

        APIManager apim = new APIManager();
        Ticker tk = apim.getTicker();

        //blablabla, do some work

        ssUpdated.Text = "Last updated: " + DateTime.Now.ToString();
        ssStatus.Text = "";
        btnRefresh.Text = "Refresh";
        btnRefresh.Enabled = true;
    }

    private void cbxRefresh_CheckedChanged(object sender, EventArgs e)
    {
        if (cbxRefresh.Checked)
        {
            Thread thread1 = new Thread(() => BGRefreshThread(Convert.ToInt32(nupRefreshSecs.Value)));
            thread1.Start();
        }
        else
        {
          // IF REFRESH CHECKBOX IS UNCHECKED, STOP THE THREAD THAT IS REFRESHING
        }
    }

    private void BGRefreshThread(int delay)
    {
        refreshTicker();
        System.Threading.Thread.Sleep(delay * 1000);
    }

My main problem is in the cbxRefresh_CheckedChanged method, basically, how this works is that when the user check the "Auto refresh" checkbox in the main UI, the checkedchanged method will create a new thread BGRefreshThread that runs in the background and refresh the ticker, and once the checkbox is unchecked again, it will end the thread that refreshes the ticker.

However, I am having problem ending the thread once it is started, since once the checkedchanged method ends, the thread no longer exists in the context when the checkbox is unchecked the next time.

Can anybody advice how I can get this working? Really new to multi-threading programming.

**EDIT: I've found a solution for this problem, but right now, when the newly created thread tries to call "refreshTicker" which updates labels and buttons on the main UI (which is on the main thread), it gives me this error:

Cross-thread operation not valid: Control 'btnRefresh' accessed from a thread other than >the thread it was created on.

any advice on this?**

InnovativeDan
  • 1,067
  • 2
  • 8
  • 10
  • Store the thread1 in a higher scope?m Use signalling, Use that Task library. – Marvin Smit Nov 22 '13 at 13:05
  • You can't access any UI control from other thread than the UI thread, you need to use `Control.Invoke` or the Dispatcher. See [here](http://stackoverflow.com/questions/5037470/cross-thread-operation-not-valid) for more info – Sebastian Piu Nov 22 '13 at 13:15
  • thanks! I changed "refreshTicker();" to "this.Invoke((MethodInvoker)delegate() { refreshTicker(); });" but the ticker still doesn't seem to refresh, seems like the thread is not running for some reason. – InnovativeDan Nov 22 '13 at 13:43

2 Answers2

1

First of all, the thread variable should be an instance member so you have access to it at any time.

What I do to stop threads gracefully is: Before I create and start the thread I create a ManualResetEvent, which I use to signal the thread to quit:

private ManualResetEvent m_stopThread = new ManualResetEvent(false);

To stop the thread I set the event somewhere in my code and wait for the thread to end:

m_stopThread.Set();
m_myThread.Join();

The thread's code needs to account for stopping:

while (true)
{
    // Stop the thread if the handle is set after 1 ms
    if (m_stopThread.WaitOne(1, false))
        break;

    // Do some work
}
Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139
  • Now I have a bigger problem, I've updated the question above with an edit, can anyone help me with it? – InnovativeDan Nov 22 '13 at 13:14
  • You can not access components on the UI thread from any other thread. This has been answered here a zillion times. Use `this.Invoke` (Windows Forms) or `this.Dispatcher.Invoke` (WPF) to change back to the UI thread. – Thorsten Dittmar Nov 22 '13 at 13:18
0

You need to save the thread in a variable that does not go out of scope.

private Thread thread1;

 private void cbxRefresh_CheckedChanged(object sender, EventArgs e)
    {
        if (cbxRefresh.Checked)
        {
            thread1 = new Thread(() => BGRefreshThread(Convert.ToInt32(nupRefreshSecs.Value)));
            thread1.Start();
        }
        else
        {
          thread1.Abort();
        }
    }

Please note that there are some checks missing and abort is not a nice way to end a thread.

nvoigt
  • 75,013
  • 26
  • 93
  • 142