views:

122

answers:

3

I am learning about threading and multithreading..so i just created a small application in which i will update the progressbar and a static text using threading.I vl get two inputs from the user, start and end values for how long the loop should rotate.I have 2threads in my application.

Thread1- to update the progressbar(according to the loop) the static text which will show the count(loop count). Thread2 - to update the another static text which will just diplay a name

Basically if the user clicks start, the progressbar steps up and at the same time filecount and the name are displayed parallely. There's is another operation where if the user clicks pause it(thread) has to suspend until the user clicks resume. The problem is,the above will not work(will not suspend and resume) for both thread..but works for a singlw thread. Please check the code to get an idea and reply me what can done!

on button click start

void CThreadingEx3Dlg::OnBnClickedStart()
{
    m_ProgressBar.SetRange(start,end);
    myThread1 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction1,this);
    myThread2 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction2,this);
}

thread1

UINT MyThreadFunction1(LPARAM lparam)
{
    CThreadingEx3Dlg* pthis = (CThreadingEx3Dlg*)lparam;
    for(int intvalue =pthis->start;intvalue<=pthis->end; ++intvalue)
    {
        pthis->SendMessage(WM_MY_THREAD_MESSAGE1,intvalue);
    }
    return 0;
}

thread1 function

LRESULT CThreadingEx3Dlg::OnThreadMessage1(WPARAM wparam,LPARAM lparam)
{
    int nProgress= (int)wparam;
     m_ProgressBar.SetPos(nProgress);
       CString strStatus;
    strStatus.Format(L"Thread1:Processing item: %d", nProgress);
        m_Static.SetWindowText(strStatus);
    Sleep(100);
    return 0;
}

thread2

UINT MyThreadFunction2(LPARAM  lparam)
{
    CThreadingEx3Dlg* pthis = (CThreadingEx3Dlg*)lparam;
    for(int i =pthis->start;i<=pthis->end;i++)
    {
        pthis->SendMessage(WM_MY_THREAD_MESSAGE2,i);
    }
    return 0;
}

thread2 function

LRESULT CThreadingEx3Dlg::OnThreadMessage2(WPARAM wparam,LPARAM lparam)
{
        m_Static1.GetDlgItem(IDC_STATIC6);
        m_Static1.SetWindowTextW(L"Thread2 Running");
        Sleep(100);
        m_Static1.SetWindowTextW(L"");
        Sleep(100);
        return TRUE;

}

void CThreadingEx3Dlg::OnBnClickedPause()
{
    // TODO: Add your control notification handler code here
    if(!m_Track)
    {
        m_Track = TRUE;
        GetDlgItem(IDCANCEL)->SetWindowTextW(L"Resume");
        myThread1->SuspendThread();
        WaitForSingleObject(myThread1->m_hThread,INFINITE);
        myThread2->SuspendThread();
        m_Static.SetWindowTextW(L"Paused..");

    }
    else
    {
        m_Track = FALSE;
        GetDlgItem(IDCANCEL)->SetWindowTextW(L"Pause");
        myThread1->ResumeThread();
        myThread2->ResumeThread();

        /*myEventHandler.SetEvent();
        WaitForSingleObject(myThread1->m_hThread,INFINITE);*/
    }
}
A: 

Windows will not operate properly when more than one thread interacts with the GUI. You'll need to reorganize your program so that does not happen.

Mark Ransom
I really appreciate that u replied..I have 2 questions regarding ur answer now1.Can we use 2 or more threads to perform an operation like reading files or checking contents in the file in which the thread is not related to GUI?2.Please tell me an alternative way of how it can be done?
kiddo
@kiddo, Yes, manipulating non-GUI "things" from a background thread is fine. An alternative approach is to use `PostMessage` instead of `SendMessage`. This causes the message to be added to the main thread's message queue to be handled there, rather than processed immediately by the calling thread -- although as I pointed out in my comment above, something like this is probably better handled with a timer.
Nick Meyer
@kiddo If you use `PostMessage`, you'll also want to move the `Sleep` calls out of the message handler function and into the thread function, lest you put the main thread to sleep :)
Nick Meyer
okay,am not sure about how to use timer..Is it like calling the threads one by one after a particular time using SetTimer,,can u show me an example
kiddo
and also I tried to replace SendMessage with postMessage..thing is, one thread will run at-a-time..which i guess not actual threading
kiddo
@kiddo Timers don't use threads. They're a way of telling the OS "notify me after this many milliseconds have elapsed". When the timer elapses, Windows posts a WM_TIMER message to your window. You're already in the main thread, so you can update the controls from there. See http://www.functionx.com/visualc/controls/timer.htm
Nick Meyer
okay,I gotch now,But like i sadi if I want to do some file operations or something like that without dealing with controls...can i use the same or it can be handled in thread.I am curious of doing that using thread,is there somelike CEvent can help in this situation.
kiddo
+3  A: 

I thought I should summarize some of the discussion in the comments into an answer.

In Windows programming, you should never try to manipulate a GUI control from a background thread, as doing so can cause your program to deadlock . This means only the main thread should ever touch elements of the GUI. (Technically, what matters is which thread created the control, but it's not common to create controls in background threads).

This requirement is detailed in Joe Newcomer's article on worker threads (see "Worker Threads and the GUI II: Don't Touch the GUI").

You are using SendMessage in your thread procedures. This causes the appropriate message handler for the target control to be invoked, but in the thread that called SendMessage. In your case, that means the background threads run the message handlers and therefore update the progress bar and label.

The alternative is to use PostMessage. This causes the message to be added to a queue to be processed by the main thread's message loop. When the main thread gets to run, it processes the messages in the order they were added to the queue, calling the message handlers itself. Since the main thread owns the windows, it is safe for it to update the controls.

You should also beware that SuspendThread and ResumeThread are tricky to get right. You might want to read this section of Joe Newcomer's article, which describes some of the dangers.


Tasks like this are often better achieved by using a timer. This is a mechanism for having the operating system notify your program when a particular amount of time has passed. You could implement this with a timer as below:

BEGIN_MESSAGE_MAP(CThreadingEx3Dlg, CDialog)
   ON_WM_DESTROY()
   ON_WM_TIMER()
END_MESSAGE_MAP()

void CThreadingEx3Dlg::OnTimer(UINT_PTR nTimerID)
{
   static int progress = 0;
   if (nTimerID == 1)
   {
      m_ProgressBar.SetPos(progress);
      CString strStatus;
      strStatus.Format(_T("Processing item: %d"), progress);
      m_Static.SetWindowText(strStatus);
      progress++;
      if (progress > end) // If we've reached the end of the updates.
         KillTimer(1);
   }
}

BOOL CThreadingEx3Dlg::OnInitDialog()
{
   // ... initialize controls, etc, as necessary.
   SetTimer(1, 100, 0);
}

void CThreadingEx3Dlg::OnDestroy()
{
   KillTimer(1);
}

If you want both updates handled at the same time, they can use the same timer. If they need to happen at different times (such as one at a 100 ms interval and another at a 150 ms interval) then you can call SetTimer twice with different IDs. To pause the action, call KillTimer. To resume it, call SetTimer again.

Nick Meyer
TBH There is no danger with making updates from different threads IF you know what you are doing. I do it all the time and have no problems :)
Goz
+1  A: 

Multi-threading and message queuing is quite a complex game. When you SendMessage from ThreadA to the same thread then it just calls the message handler. If you do it from ThreadA to another thread (ThreadB) then it gets more complicated. ThreadA then posts a message to the ThreadB's message queue and waits on a signal to say that ThreadB has finished processing the message and sent the return value. This raises an instant problem. If ThreadB is not pumping messages then you have a deadlock as the message in ThreadB's will never get "dispatched". This also raises an EVEN bigger problem. If ThreadB's message needs to send a message to a control created in ThreadA then you have a massive architectural problem. As ThreadA is currently suspended waiting for ThreadB to return and ThreadB is suspended waiting for ThreadA to return. Nothing will happen ... They will both sit suspended.

Thats about it really. Its pretty easy as long as you bear these issues in mind. ie It absoloutely IS possible despite what the others have said.

In general though your threading is pretty pointless because you straight away send a message to the main thread to do some processing. Why bother starting the threads in the first place. You may as well not bother because the threads will just sit there waiting for the main thread to return.

Why do you "WaitForSingleObject" anyway when you suspend the first thread? Why not just suspend them both.

All round, though, you aren't giving enough information about what you are doing to say exactly whats going on. What happens when you click pause, for example?

Goz