![]() |
Проблема с многопоточностью, C++
В общем интересуюсь этой проблемой уже давно. Пишу парсер аккаунтов одного форума. Пока тестовый, в 5 потоках надо отпарсить только 5 юзеов. Есть код
Код:
int uid = 1; //текущий айди юзера |
2mailbrush
а с чего ты взял, что потоки создадутся, запустятся и дойдут до sprintf сразу после _beginthread, там же ещё хренова туча crt мутотени выполнять? Есественно, что цикл выполняется быстрее, чем uid используется в потоке. В данном случае нужно синхронизировать потоки или передавать индивидуальные параметры по типу: int *q = malloc(5*4); for(i=0;i<=4;i++) { uid++; q[i] = uid; _beginthread(tfunc, 0, &q[i]); } free(q); PS НЕ ИСПОЛЬЗУЙ _beginthread БОЛЬШЕ НИКОГДА!!! ОНА УСТАРЕЛА!!! ИСПОЛЬЗУЙ _beginthreadex. |
У меня раньше тоже была подобная проблема. Она решается путем блокировки переменной. В общем, я читал на блоге Kaimi статью от DX по работе с антикапчей. Если я не ошибаюсь, в статье такая проблема решается с помощью мьютексов.
http://kaimi.ru/2009/05/anticaptcha-assembler/ (ASM) |
а вообще для таких целей есть интерлоковые функции которые юзаются внутри потока. типа такой:
Код:
К томуже IntToStr(uid).c_str() смысл юзать? или можно с шаблоне задать %i или %u |
2slesh
Интерлокед конечно быстрые, да и в большинстве случаев синхронизация концептуальнее более правильна, но имхо в брутерах-парсерах многопоточных лучше инициализировать все заранее + использовать пулы потоков (+ асинхронный I/O). Тогда к моменту исполнения непосредственно функционала не надо будет тратится на создания потоков (тысячи тактов) и/или синхронизацию (от десятков до сотен тактов). Выйгрышь в производительности существеннен, оссобенно при количестве потоков более 2*кол-во_процессоров. Вышесказанное естественно ИМХО. |
всё это правильно, но ты забыл про одно - скорость сети - ничтожна по сравнению с пропускной способностью шины памяти и скорости работы проца.
Так что как никрути но всё равно упрешся в скорость сети. И эти все затраты будут незаметны. темболее что если ты не заметил то интерлок функции состоят примерно из 3-4 машинных команд. и такая синхронизация никаких затрат не вызывает. банальный тест: на проце E5200 (2 ядра 2,5 ГГц) 10 миллионов вызовов InterlockedIncrement занимают ~156 микросекунд. так что за 1 микросекунду эту будет примерно 64 тысячи раз. При этом если учеть что идеальаня скорость инета - 100 мегобит / с то выходит что за 1 микросекунду максимум ты пошлеш только 12500 байт. Это только посылка. Сам понимаеш такой инет мало у кого есть. а вот такие процы есть у всех. так что сейчас как никрути но всё только в сеть упирается. единственное что для брута нужно - выделенная память для буферов - вот это реально критично. потому что выделение памяти - процес довольно медленный. Потому что 10 тысяч выделений памяти по 64 кила. занимает времяни больше 1 секунды. Да и брутеры не нужнаются в такой скорости по причине того что тебя быстрее забанять на серваке ) Другое дело - управление сетевыми приложениями или ботнетами. вот тут вот нагрузка довольно большая идет, но и то с ней достаточно весомо справляется проц. а вот канал уже дохнет |
2slesh
все мы стремимся к идеалу )) поэтому и пишем shl eax, 1 вместо mul, хотя вызовы API сотрут различия )) тем более не стоит забывать, что при увеличении числа потоков прога станет быстрее (в смысле в работе на хосте) за счет того, что все тяжелые операции мы сделаем заранее. ИМХО ^____^ |
Код:
//--------------------------------------------------------------------------- |
WSAStartup - делается только в начале проги и тока 1 раз
WSACleanup - только в конце проги. Так что таким видом ты можеш заглючить прогу |
Цитата:
|
2mailbrush
у тебя с логикой программы не в порядке. Ты смотри где интерлокед вызываешь. Глобальная переменная увеличится только пять раз, по числу создаваемых потоков, а в цикле её значение аж с 15 сравнивается. да и это PHP код:
|
Цитата:
|
2mailbrush
дык в цикле и делай, только память динамически выделяй. чтото типа int i = 0; char *buf = calloc(1024); while(recv(s, &buf[i], 1024, 0)) { ... i += 1024; buf = realloc(buf, 1024 + i); } free(buf); PS и не забывай ещё обрабатывать ошибки recv |
Поставил внутрь цикла while инкремент - первых пять потоков обрабатывают 5й айди, вторые 10, третье 15 и т.д... Эх...
Hiro Protagonist, меня пока интересует инкремент. В любом случае спасибо всем за помощь! |
2mailbrush
я вообще не понимаю зачем тебе этот цикл. Делай так. чтобы один id обрабатывал один поток и умирал. |
Цитата:
|
вот вы фанаты-изобретатели-мазохисты. ну сделайте вы у функции обработки потока параметр. и скидывайте значение счетчика i. ибо без понимания, почему не канает вариант с глобальной переменной, изобретать вокруг него огород - тупо.
или юзайте мьютексы для ожидания момента окончательного считывания значения глобальной переменной в обработчике потока. |
Цитата:
|
гугл "beginthread msdn" первая ссылка. скролишь пример и читаешь. правда сложно?
если чтото не понятно - спрашивай. тока леницо не надо. ps: проще для понимания второй пример. если бы использовал _beginthreadex вопроса о параметрах не возникло бы. |
Цитата:
|
2Ra$cal
мьютексы намного тормознее интерлокеда и в этом примере не надо их применять. 2mailbrush 1. Ааа, я понял. Тебе нужно чтобы несколько потоков обрабатывали некий диапазон. Дык разбивай диапазон этот на части и передавай поддиапазон (начало/размер) в поток. Там локальной переменной присваивай начальный uid диапазона и в цикле проходи все. Тогда тебе ненужна синхронизация и глобальная переменная. А то в начале у тебя код другое подразумевал ). 2. пример с передачей параметров в поток я приводил на первой странице. |
Цитата:
|
Цитата:
Функция, которая вызывает поток: Код:
void __fastcall TForm1::Button1Click(TObject *Sender)Код:
volatile long uid = 1;Код:
void tfunc(void *argv)Буду преблагодарен за помощь... |
Код:
#include <iostream>0 2 4 3 1 Press any key to continue . . . |
Цитата:
утяж аргумент argv == NULL в beginthread, а вообще лучше - апи) |
| Время: 07:00 |