Лабораторная работа 5.1

Задание:

Написать программу в которой “одновременно” выполняются два потока. Процесс выполнения каждого потока должен отражаться при помощи индикатора.

Ход работы:

  1. Создание проекта. Создайте обычный проект, по схеме описанной на предыдущих уроках и назовите его L5_1.
  2. Создание ресурса диалогового окна. Добавьте новый ресурс диалогового окна (Рис. 5.1.1).
  3.  

  4. Создание класса диалогового окна. Создайте новый класс, связанный с данным диалоговым окном и назовите его CMyDlg.
  5. Программирование класса диалогового окна. Добавьте следующие строки в заголовочный файл Вашего класса
  6. public:

    // Установка максималиных значений для потоков

    enum {nMax1=1000000,nMax2=500000};

    //Переменная для работы с таймером

    private:

    int m_nTimer;

    Создайте две функции, которые будут представлять два рабочих потока, и которые будут общаться с основным потоком Вашей программы через глобальные переменные.

    //Глобальные переменные отражающие состояние потоков

    int m_nCount1=0;

    int m_nCount2=0;

    //Первый поток

    UINT Potok1 (LPVOID pParam)

    {

    //Для того чтобы компилятор не хранил

    //счетчик в регистре используестся служебне слово

    //volatile

    volatile int nTmp;

    // Внешний цикл который выполняется 1000000 раз.

    // Функция InterlockedIncrement запрещает дступ к переменной

    // во время ее увеличения.

    for (m_nCount1=0; m_nCount1<CMyDlg::nMax1; ::InterlockedIncrement((long*)&m_nCount1))

    {

    //Пустой цикл. Просто танем время

    for (nTmp=0; nTmp<=1000; nTmp++) {}

    }

    // В конце своей работы востанавливаем

    // начальное значение переменной

    m_nCount1=0;

    // Поток возвращает 0

    return 0;

    }

    // Второй поток

    UINT Potok2 (LPVOID pParam)

    {

    //Для того чтобы компилятор не хранил

    //счетчик в регистре используестся служебне слово

    //volatile

    volatile int nTmp;

    // Внешний цикл который выполняется 1000000 раз.

    // Функция InterlockedIncrement запрещает дступ к переменной

    // во время ее увеличения.

    for (m_nCount2=0; m_nCount2<CMyDlg::nMax2; ::InterlockedIncrement((long*)&m_nCount2))

    {

    //Пустой цикл. Просто танем время

    for (nTmp=0; nTmp<=1000; nTmp++) {}

    }

    // В конце своей работы востанавливаем

    // начальное значение переменной

    m_nCount2=0;

    // Поток возвращает 0

    return 0;

    }

    Добавьте обработчики для кнопок диалогового окна.

    // Функция выполняется когда нажимают кнопочку "Старт

    void CMyDlg::OnOK()

    {

    // Устанавливаем значение таймера 1/100 секунды

    m_nTimer=SetTimer(1,100,NULL);

    // Проверка на правильность

    ASSERT (m_nTimer!=0);

    // Далаем кнопку "Старт" неактивной

    GetDlgItem(IDOK)->EnableWindow(FALSE);

    // Создаем первый поток с нормальнам приоритетом

    AfxBeginThread (Potok1,GetSafeHwnd(), THREAD_PRIORITY_NORMAL);

    // Создаем второй поток с нормальнам приоритетом

    AfxBeginThread (Potok2,GetSafeHwnd(), THREAD_PRIORITY_NORMAL);

    }

    // Функция выполняется когда нажимают кнопочку "Стоп"

    void CMyDlg::OnStop()

    {

    // Если потоки еще не начали работать, то

    if (m_nCount1==0 && m_nCount2==0)

    {

    //Кнопка работает как кнопка Cancel

    CDialog::OnCancel();

    }

    // В противном случае

    else

    {

    // завершаем потоки

    m_nCount1=nMax1;

    m_nCount2=nMax2;

    // и делаем кнопочку "Старт" активной

    GetDlgItem(IDOK)->EnableWindow(TRUE);

    }

    }

    Для того чтобы мы могли отображать текущее состояние каждого потока, нам необходимо через определенное количество времени останавливать его и в соответствии с переменными отражающими состояние каждого потока обновлять информацию на экране. Удобнее всего это делать в обработчике сообщения WM_TIMER.

    // Функция выполняется через интервал

    // установленный функцией SetTimer.

    void CMyDlg::OnTimer(UINT nIDEvent)

    {

    //Получаем указатели по индикаторы выполнения процесса

    CProgressCtrl* pBar1=(CProgressCtrl*) GetDlgItem(IDC_PROGRESS1);

    CProgressCtrl* pBar2=(CProgressCtrl*) GetDlgItem(IDC_PROGRESS2);

    // Установливаем текущии позиции в соответствии

    // с текущим значением переменных определяющих

    // состояние потока

    pBar1->SetPos(m_nCount1*100/nMax1);

    pBar2->SetPos(m_nCount2*100/nMax2);

    // Если оба потока завершены

    if (m_nCount1==0 && m_nCount2==0)

    {

    // то сделать кнопочку "Старт" активной

    GetDlgItem(IDOK)->EnableWindow(TRUE);

    }

    }

  7. Добавление пункта меню. После того как все сделано, необходимо добавить в главное меню пункт “Потоки”, и в обработчике его командного сообщения, в классе CL5_1View, написать код вызывающий созданное диалоговое окно (Не забудьте подключить созданный класс диалогового окна к классу CL5_1View).
  8. // Обработчик командного сообщения

    // от пункта меню "Потоки"

    void CL5_1View::OnMenuProc()

    {

    // Создание экземпляра класса CMyDlg

    CMyDlg ddd;

    // Запуск модального окна.

    ddd.DoModal();

    }

  9. Домашнее задание. Модифицируйте данную программу таким образом, чтобы рабочие потоки общались с основным потоком через события по механизму, описанному в лекциях. Добавьте в каждый поток функциональность. Пусть первый поток ищет числа Фибоначи, а второй простые числа.