Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Потоки


Автор: DareL 9.3.2009, 13:14
Посоветуйте, пожалуйста.
Прохожу WinApi.
Дали следующее заданик:

Цитата

Разработать многопоточное приложение, осуществляющее генерирование случайных чисел с нормальным законом распределения. Предусмотреть возможность сохранения выборок этих чисел в файлах и обработки чисел (например,  сортировку).  Подобрать примеры, которые наглядно демонстрируют работу созданного приложения. Оценить преимущества использования нескольких потоков для работы данного приложения.


Где и как тут можно использовать потоки?

Автор: Lazin 9.3.2009, 13:39
делишь массив на N частей, первую часть заполняешь из первого потока, вторую из второго и тд...

Автор: ilyuha 9.3.2009, 13:44
а что конкретно не понятно, какие подзадачи выделять в отдельные потоки, или, непосредственно, сама реализация?

Автор: GoldFinch 9.3.2009, 14:03
Цитата(DareL @  9.3.2009,  13:14 Найти цитируемый пост)
Оценить преимущества использования нескольких потоков для работы данного приложения.

какие могут быть преимущества у такого надуманного примера %)

Автор: DareL 9.3.2009, 14:15
Цитата(ilyuha @  9.3.2009,  13:44 Найти цитируемый пост)
а что конкретно не понятно, какие подзадачи выделять в отдельные потоки, или, непосредственно, сама реализация?

Какие подзадачи выделить.
С реализацией я, надеюсь, разберусь

Автор: zim22 9.3.2009, 14:57
потоки можно использовать на этапе сортировки.
первый поток один кусок массива сортирует, второй - другой.

Автор: Artemon 10.3.2009, 13:29
Просто сделай в потоке генерацию случайных числел, а в главном потоке выводи их на форму.

Вот тебе и 2 потока.

Подсказка:

Межпоточное взаимодействие сделай через WM_COPYDATA

Автор: GoldFinch 10.3.2009, 14:10
Цитата(Artemon @  10.3.2009,  13:29 Найти цитируемый пост)
WM_COPYDATA 

зачем такой изврат %) подойдет обычная глобальная структура типа массива

вся выборка сгенерится быстрее чем сработает WM_COPYDATA

Автор: Artemon 10.3.2009, 17:16
Если мы используем глобальные объекты для потоков, то необходима синхронизация.
Отсюда следует что топикстартеру нужно разобираться с межпроцессным взаимодействием, а я думаю это больший геморой, чем использовать WM_COPYDATA в данном случае.

Автор: Lazin 10.3.2009, 18:07
Цитата(Artemon @  10.3.2009,  17:16 Найти цитируемый пост)
Если мы используем глобальные объекты для потоков, то необходима синхронизация.

не обязательно
если к примеру нужно просто заполнить массив случайными числами, то
1. определяем число процессоров - cpu_count
2. делим массив на cpu_count частей
3. создаем cpu_count потоков, в качестве параметра, передаем в каждый поток начальный и конечный индексы массива, в которые поток должен записывать данные, а так-же указатель на сам массив( либо пару указателей)
4. ждем завершения потоков
5. получаем прирост производительности cpu_count*100% - x
Quick sort и merge sort то-же неплохо можно распараллелить...

Автор: GoldFinch 10.3.2009, 19:26
Цитата(Artemon @  10.3.2009,  17:16 Найти цитируемый пост)
с межпроцессным взаимодействием,

каким еще межпроцессным? там потоки а не процессы

Автор: Artemon 11.3.2009, 07:13
Lazin, твой способ подойдет только для случая, когда мы один раз заполним массив и закончим потоки, НЕ выводя циклически данные в основной поток. Иначе нам всеравно придется использовать синхронизацию.

Цитата

каким еще межпроцессным? там потоки а не процессы


Конечноже межпоточным, опечатался.

Автор: GoldFinch 11.3.2009, 09:08
Artemon, синхронизация осуществляется оборачиванием кода в критические секции, это куда как проще и прозрачнее чем посылать сообщения
и где вообще сказано что в программе должно быть окно?

Автор: Lazin 11.3.2009, 09:39
Цитата(DareL @  9.3.2009,  13:14 Найти цитируемый пост)
Разработать многопоточное приложение, осуществляющее генерирование случайных чисел с нормальным законом распределения.

основной поток запускает потоки и ждет пока они отработают, после чего может дальше работать, зачем здесь синхронизация?
Код

#include <iostream>
#include <stdio.h>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/thread/thread.hpp>
#include <algorithm>
#include <windows.h>


int get_cpu_count()
{
    SYSTEM_INFO info;
    ZeroMemory(&info, sizeof(SYSTEM_INFO));
    GetSystemInfo(&info);
    return info.dwNumberOfProcessors;
}


int main()
{
    std::vector<int> random_samples(100000);
    int cpu_count = get_cpu_count();
    boost::thread_group tasks;
    for(int i = 0; i < cpu_count; ++i)
    {
        tasks.create_thread( boost::bind( &std::generate<std::vector<int>::iterator, int ( void ) >
                                        , random_samples.begin() + (i*random_samples.size())/cpu_count
                                        , random_samples.begin() + ((i + 1)*random_samples.size())/cpu_count
                                        , &rand) 
                                        );
    }
    tasks.join_all();
}

Автор: C/L 11.3.2009, 10:27
Цитата(Lazin @  11.3.2009,  11:39 Найти цитируемый пост)
ждет пока они отработают

наверно это и есть синхронизация, только посредством tasks.join_all().

Автор: Lazin 11.3.2009, 10:35
Цитата(C/L @  11.3.2009,  10:27 Найти цитируемый пост)
наверно это и есть синхронизация, только посредством tasks.join_all()

рабочие потоки в данном случае синхронизировать ну нужно, только основной поток

Автор: Artemon 11.3.2009, 11:54
Цитата

синхронизировать ну нужно,

Об этом и речь smile

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


Цитата

Artemon, синхронизация осуществляется оборачиванием кода в критические секции, это куда как проще и прозрачнее чем посылать сообщения


Это субъективное мнение.

Цитата

и где вообще сказано что в программе должно быть окно?


Про окно значит не сказано, а boost  значит впорядке вещей получается ?


Автор: mrbrooks 11.3.2009, 12:07
Цитата(Artemon @  11.3.2009,  11:54 Найти цитируемый пост)
Про окно значит не сказано, а boost  значит впорядке вещей получается ?

у Lazina - да. Я к этому уже привык.  smile 

Автор: Lazin 11.3.2009, 12:25
Цитата(Artemon @  11.3.2009,  11:54 Найти цитируемый пост)
но как вы поступите, если нам постоянно потребуется вести расчет в потоках и постоянно выводить результаты, это я к тому, что не возможно этот сделать без синхронизации

речь идет об одном конкретном примере, если будет что-то другое, то будет и другое решение. В данном случае, пока потоки не отработают, с массивом нельзя работать и никакие расчеты невозможны. Если, к примеру, нам нужно отсортировать полученный массив, нам придется ждать генерации всех элементов массива, затем начинать сортировку. Можно вместо сортировки вставлять элементы в бинарное дерево поиска, например std::set, тогда сортировать не нужно, но тогда нужно будет синхронизировать доступ к этому дереву.

Добавлено через 1 минуту и 4 секунды
в любом случае, обмен сообщениями между потоками - не самый эффективный механизм, особенно когда объем обрабатываемых совместно данных велик

Добавлено через 3 минуты и 35 секунд
кстати, для того что-бы использовать WM_COPYDATA нужно создавать окно, послать такое сообщение потоку нельзя
вместо этого, я бы завел очередь, в одном потоке добавлял в нее данные, а из другого читал, ну и естественно оповещал бы поток - потребитель о приходе новых данных с помощью события...

Автор: Artemon 11.3.2009, 12:43
Да, большой объем прогонять через WM_COPYDATA не эффективно.
Но еслиже у нас маленькое приложение и объем данных мизерный, то моему лучше сделать чрез WM_COPYDATA.
Просьба не начинать спор с начала, думаю что все остались при своих мнениях.

Автор: GoldFinch 11.3.2009, 13:00
Artemon, какие тогда преимущества дает многопоточность, если юзать WM_COPYDATA?

Автор: Lazin 11.3.2009, 13:20
естественно никакие, так-как это сообщение можно послать только с помощь ф-ии SendMessage, которая работает синхронно... если и посылать сообщения потокам, то только через PostThreadMessage

Автор: mrbrooks 11.3.2009, 14:00
На самом деле тему раздули - молодцы. Не проще ли автору посоветовать почитать Рихтера главу про потоки и их синхронизацию? На мой взгляд все достаточно доступно и доходчиво.

Автор: Artemon 11.3.2009, 14:12
1. Если мы пишем для Windows, используя к примеру WinApi, у нас уже и так есть оконная процедура и с минимальными усилиями мы можем добавить обработку собщения WM_COPYDATA.

2. Если нам необходимо вывести данные из второстепенных потоков в окно.

Цитата

естественно никакие, так-как это сообщение можно послать только с помощь ф-ии SendMessage,


И что с того, что можно послать только через SendMessage ?
Если нужно работать с GUI, это очень даже приемлимый способ.

У меня создалось впечатление, что вы думаете мое предложение - использовать взаимодействие через WM_COPYDATA между второстепенными потоками, нет это не так. Я передлагаю использовать WM_COPYDATA только для взаимодействия второстепенного и главного потоков для дальнейшего вывода информации в GUI.

Автор: GoldFinch 11.3.2009, 14:21
Artemon, кроме GUI прог в винде есть еще и консольные проги, а еще есть проги без окна и без консоли

Автор: Artemon 11.3.2009, 14:34
Неужели? А еще есть драйвера, работающие в нулевом кольце. Которые тоже могут использовать потоки.

Только я не пойму вообще к чему это ты сказал.

Автор: Lazin 11.3.2009, 14:50
Цитата(Artemon @  11.3.2009,  14:34 Найти цитируемый пост)
Неужели? А еще есть драйвера, работающие в нулевом кольце. Которые тоже могут использовать потоки.

вообще-то, даже в GUI приложениях в основном потоки используют общие данные и синхронизируют их с помощью различных примитивов синхронизации, и ооочень редко используется WM_COPYDATA smile 
WM_COPYDATA обычно используют для организации взаимодействия процессов, тогда, когда оно должно-быть синхронным.

Автор: Artemon 11.3.2009, 14:58
Вот ты мне объясни, зачем мне нужна синхронизация, если я один раз из потока в окно хочу одну строку вывести ? Мне очень удобно это сделать через SendMessage + WM_COPYDATA

Реальный пример:

Программа, которая производит спам рассылку, у которой 4 потока:

1. Сбор IP
2. Проверка IP (является ли этот адрес прокси сервером)
3. Распознавание капчи
4. Основной поток.

Все потоки синхронизованы через Mutex-ы

И вот например случается событие в потоке проверки IP (№2), о котором мне необходимо информировать пользователя. Я беру и кидаю строку через SendMessage + WM_COPYDATA, в главном окне ловлю его и отображаю в поле статистики.

Зачем мне сдалась какая-либо синхронизация, касающаяся вывода сообщений пользователю.
//-------------------------------------------------------------------------------------------
Про автора топика совсем забыл, у тебя GUI или консольное приложение ?
Если GUI, то советую сделать так, как я предложил, если консольное - возьми исходник Лазина.

Автор: GoldFinch 11.3.2009, 15:20
Artemon, ты жжошь
безусловно формошлепство накладывает некоторый оконный отпечаток на личность)

Добавлено через 1 минуту и 2 секунды
еще можно отправлять данные на почту по POP3 и читать от туда другим потоком

Автор: Lazin 11.3.2009, 15:33
Цитата(GoldFinch @  11.3.2009,  15:20 Найти цитируемый пост)
еще можно отправлять данные на почту по POP3 и читать от туда другим потоком 

я хотел предложить записывать данные в файл одним потоком и читать их оттуда другим, но ты меня обошел))
Цитата(MSDN)

An application sends the WM_COPYDATA message to pass data to another application.
еще вопросы? smile

Добавлено через 3 минуты и 24 секунды
Цитата(Artemon @  11.3.2009,  14:58 Найти цитируемый пост)
Программа, которая производит спам рассылку, у которой 4 потока:

1. Сбор IP
2. Проверка IP (является ли этот адрес прокси сервером)
3. Распознавание капчи
4. Основной поток.

Все потоки синхронизованы через Mutex-ы

И вот например случается событие в потоке проверки IP (№2), о котором мне необходимо информировать пользователя. Я беру и кидаю строку через SendMessage + WM_COPYDATA, в главном окне ловлю его и отображаю в поле статистики.

Зачем мне сдалась какая-либо синхронизация, касающаяся вывода сообщений пользователю.

с данной программой все просто, ее просто не надо писать smile 

Цитата(Artemon @  11.3.2009,  14:58 Найти цитируемый пост)
Если GUI, то советую сделать так, как я предложил, если консольное - возьми исходник Лазина.

блин, откуда вы все знаете мою фамилию? smile 

Автор: Artemon 11.3.2009, 15:41
Если вы мне предложите более лаконичное решение примера, который я привел (конечно же обоснованное решение), то я соглашусь с вами, сейчас получается все на уровне разговора без аргументов (особенно GoldFinch)

Добавлено через 1 минуту и 54 секунды
Цитата

с данной программой все просто, ее просто не надо писать   smile 


Ну ты тоже скажешь, это мой хлеб практически.

Автор: GoldFinch 11.3.2009, 15:46
Цитата(Lazin @  11.3.2009,  15:33 Найти цитируемый пост)
блин, откуда вы все знаете мою фамилию?

тут полфорума - экстрасенсы

синхронизация через критические секции выглядит так

Код

... foo(...) //функция или метод которая должна быть синхронной
{
  Synchronizer SinchronizeMe(); //заклинание которое оборачивает функцию в критическую секцию
  ... //тут работаем с данными (читаем или пишем)
};

- лаконичнее некуда  - 1 строчка на то чтобы сделать функцию thread-safe

неужели WM_COPYDATA в каком-то месте удобнее?

Автор: Artemon 11.3.2009, 15:55
Критическая секция - это как говорится хорошо,

Покажи мне пример в котором из вторичного потока выводятся данные в Windows окно.

Если кто-то покажет более удобный способ сделать то, о чем я говорю, то я с удовольствием буду его использовать, я ведь не враг самому себе   smile 

Автор: Lazin 11.3.2009, 16:10
Код

//общие данные
std::list<std::string> queue;
boost::mutex mutex;
UINT user_msg = RegisterWindowMessage("my message");
//поток
void thread()
{
     ...чет фигня какая-то случилась, надо сообщить пользователю...
    {
        boost::mutex::scoped_lock lock(mutex);
        queue.push_back( std::string("message") );
        PostMessage(window_handle, user_msg, 0, 0);
    }
}


//обработчик сообщений
while(GetMessage(msg, 0, 0))
{
    ....
    if(msg.message == user_msg)
    {
        std::string message;
        {
        boost::mutex::scoped_lock lock(mutex);
        message = queue.back(); 
        queue.pop_back();
        }
    ...показываем сообщение...
    }
}

Цитата(Artemon @  11.3.2009,  15:41 Найти цитируемый пост)
Ну ты тоже скажешь, это мой хлеб практически

а как-же вечные муки в аду? smile 

Автор: Artemon 11.3.2009, 16:32
Ну вот ты используешь PostMessage + Сообщение + Mutex
И чем оно лучше SendMessage + Сообщение ?
Вообще в упор не вижу никаких плюсов.

Добавлено через 33 секунды
Цитата

а как-же вечные муки в аду?  smile 


Все там будем...

Автор: Lazin 11.3.2009, 16:39
Цитата(Artemon @  11.3.2009,  16:32 Найти цитируемый пост)
Вообще в упор не вижу никаких плюсов.

твои проблемы)
Цитата(Artemon @  11.3.2009,  16:32 Найти цитируемый пост)
И чем оно лучше SendMessage + Сообщение ?

тем, что в случае SendMessage, поток вызвавший SendMessage будет ждать пока не отработает оконная процедура, а в моем коде этого не будет

Автор: Artemon 11.3.2009, 16:55
Единственное в чем с тобой согласен это:

Цитата

тем, что в случае SendMessage, поток вызвавший SendMessage будет ждать пока не отработает оконная процедура



Но опятьже, я вызываю SendMessage довольно редко и для меня это не критично.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)