Проблема с многопоточностью, C++

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by mailbrush, 25 Sep 2009.

  1. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    В общем интересуюсь этой проблемой уже давно. Пишу парсер аккаунтов одного форума. Пока тестовый, в 5 потоках надо отпарсить только 5 юзеов. Есть код
    Code:
    int uid = 1; //текущий айди юзера
    
    void tfunc(void *argv)  
    {
    //здесь я создаю WinSock
    sprintf( request, "GET /forum/profile.php?mode=viewprofile&u=%s HTTP/1.1\r\nHost: tfile.ru\r\nConnection: Close\r\n\r\n", IntToStr(uid).c_str()); //пишу в запрос id юзера 
    //здесь завершаю работу WinSock
    }
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    for(i=0;i<=4;i++)
    {
    uid++;
    _beginthread(tfunc, 0, NULL); //старт потока
    }
    }
    Проблема в том, что запрос отправляется всегда с айди = 5, т.е. с последним значением uid. Почему?
     
  2. Hiro Protagonist

    Joined:
    26 Aug 2009
    Messages:
    132
    Likes Received:
    24
    Reputations:
    -2
    2mailbrush

    а с чего ты взял, что потоки создадутся, запустятся и дойдут до sprintf сразу после _beginthread, там же ещё хренова туча crt мутотени выполнять? Есественно, что цикл выполняется быстрее, чем uid используется в потоке. В данном случае нужно синхронизировать потоки или передавать индивидуальные параметры по типу:

    int *q = malloc(5*4);

    for(i=0;i<=4;i++)
    {
    uid++;
    q = uid;
    _beginthread(tfunc, 0, &q);
    }

    free(q);

    PS НЕ ИСПОЛЬЗУЙ _beginthread БОЛЬШЕ НИКОГДА!!! ОНА УСТАРЕЛА!!! ИСПОЛЬЗУЙ _beginthreadex.
     
    #2 Hiro Protagonist, 25 Sep 2009
    Last edited: 25 Sep 2009
    1 person likes this.
  3. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    937
    Likes Received:
    162
    Reputations:
    27
    У меня раньше тоже была подобная проблема. Она решается путем блокировки переменной. В общем, я читал на блоге Kaimi статью от DX по работе с антикапчей. Если я не ошибаюсь, в статье такая проблема решается с помощью мьютексов.

    http://kaimi.ru/2009/05/anticaptcha-assembler/

    (ASM)
     
    #3 Chrome~, 25 Sep 2009
    Last edited: 25 Sep 2009
    1 person likes this.
  4. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    а вообще для таких целей есть интерлоковые функции которые юзаются внутри потока. типа такой:
    Code:
    
    int uid = 1; //текущий айди юзера
    
    void tfunc(void *argv)  
    {
    int myID =  InterlockedIncrement(&uid) - 1;
    //здесь я создаю WinSock
    sprintf( request, "GET /forum/profile.php?mode=viewprofile&u=%i HTTP/1.1\r\nHost: tfile.ru\r\nConnection: Close\r\n\r\n", my_ID); //пишу в запрос id юзера 
    //здесь завершаю работу WinSock
    }
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    for(i=0;i<=4;i++)
    {
    _beginthread(tfunc, 0, NULL); //старт потока
    }
    }
    
    т.е. значение переменной увеличивается на 1 через атомарный доступ к памяти. т.е. происходит блокировка шины памяти, то даже на многопроцессорных системах всё будет пахать офегительно.

    К томуже IntToStr(uid).c_str() смысл юзать? или можно с шаблоне задать %i или %u
     
    1 person likes this.
  5. Hiro Protagonist

    Joined:
    26 Aug 2009
    Messages:
    132
    Likes Received:
    24
    Reputations:
    -2
    2slesh

    Интерлокед конечно быстрые, да и в большинстве случаев синхронизация концептуальнее более правильна, но имхо в брутерах-парсерах многопоточных лучше инициализировать все заранее + использовать пулы потоков (+ асинхронный I/O). Тогда к моменту исполнения непосредственно функционала не надо будет тратится на создания потоков (тысячи тактов) и/или синхронизацию (от десятков до сотен тактов). Выйгрышь в производительности существеннен, оссобенно при количестве потоков более 2*кол-во_процессоров. Вышесказанное естественно ИМХО.
     
    1 person likes this.
  6. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    всё это правильно, но ты забыл про одно - скорость сети - ничтожна по сравнению с пропускной способностью шины памяти и скорости работы проца.
    Так что как никрути но всё равно упрешся в скорость сети. И эти все затраты будут незаметны.

    темболее что если ты не заметил то интерлок функции состоят примерно из 3-4 машинных команд. и такая синхронизация никаких затрат не вызывает.
    банальный тест:
    на проце E5200 (2 ядра 2,5 ГГц)
    10 миллионов вызовов InterlockedIncrement
    занимают ~156 микросекунд. так что за 1 микросекунду эту будет примерно 64 тысячи раз.
    При этом если учеть что идеальаня скорость инета - 100 мегобит / с то выходит что за 1 микросекунду максимум ты пошлеш только 12500 байт. Это только посылка.
    Сам понимаеш такой инет мало у кого есть. а вот такие процы есть у всех.
    так что сейчас как никрути но всё только в сеть упирается. единственное что для брута нужно - выделенная память для буферов - вот это реально критично. потому что выделение памяти - процес довольно медленный. Потому что 10 тысяч выделений памяти по 64 кила. занимает времяни больше 1 секунды.

    Да и брутеры не нужнаются в такой скорости по причине того что тебя быстрее забанять на серваке )

    Другое дело - управление сетевыми приложениями или ботнетами. вот тут вот нагрузка довольно большая идет, но и то с ней достаточно весомо справляется проц. а вот канал уже дохнет
     
    #6 slesh, 25 Sep 2009
    Last edited: 25 Sep 2009
  7. Hiro Protagonist

    Joined:
    26 Aug 2009
    Messages:
    132
    Likes Received:
    24
    Reputations:
    -2
    2slesh

    все мы стремимся к идеалу )) поэтому и пишем shl eax, 1 вместо mul, хотя вызовы API сотрут различия )) тем более не стоит забывать, что при увеличении числа потоков прога станет быстрее (в смысле в работе на хосте) за счет того, что все тяжелые операции мы сделаем заранее. ИМХО ^____^
     
    1 person likes this.
  8. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Code:
    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    #pragma hdrstop
    
    #include "Unit1.h"
    #include <process.h>
    #include <stdio.h>
    #include <winsock2.h>
    #include <iostream>
    #include <string>
    #include <vector>
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma link "sSkinManager"
    #pragma link "sSkinProvider"
    #pragma resource "*.dfm"
    
    TForm1 *Form1;
    volatile long uid = 1;
    char mystring [1024];
    CRITICAL_SECTION cs;
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
    }
    int i;
    
    
    
    void tfunc(void *argv)
    {
    int myID =  InterlockedIncrement(&uid) - 1;
    char request[1024*5], response[1024*5];
    int first, second;
    char* headers;
    
    sockaddr_in s_a;
    WSADATA wd;
    WSAStartup( MAKEWORD( 2, 2 ), &wd );
    
    while(myID<15)
    {
    SOCKET s = socket( AF_INET, SOCK_STREAM, 0 );
    if (s == INVALID_SOCKET)
    ShowMessage("INVALID");
    s_a.sin_addr.s_addr = inet_addr( "89.188.109.206" );
    s_a.sin_port = htons( 80 );
    s_a.sin_family = AF_INET;
    if (connect( s, (struct sockaddr*)&s_a, sizeof( s_a ) ) == SOCKET_ERROR)
    ShowMessage("Bad Connect!");
    sprintf( request, "GET /forum/profile.php?mode=viewprofile&u=%i HTTP/1.1\r\nHost: tfile.ru\r\nConnection: Close\r\n\r\n", myID);
    send( s, request, strlen( request ), 0 );
    //EnterCriticalSection( &cs );
    for(i=0;i<=4;i++)
    recv( s, response, 1024 , 0 );
    //LeaveCriticalSection( &cs );
    closesocket( s );
    }
    WSACleanup(); //
    }
    
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    InitializeCriticalSection( &cs );
    for(i=1;i<=5;i++)
    {
    _beginthread(tfunc, 0, NULL);
    }
    }
    //---------------------------------------------------------------------------
    
    
    
    
    
    
    
    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
    ShowMessage("hello");
    }
    //---------------------------------------------------------------------------
    
    Вобщем вот весь код... Теперь я хочу, чтобы каждый поток своего юзера парсил, но что я только не плюсовал, чтобы айди увеличивался - ничего не помогает. Все потоки парсят 1-5 юзера. Да и с цикла while() не выходит...
     
  9. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    WSAStartup - делается только в начале проги и тока 1 раз
    WSACleanup - только в конце проги. Так что таким видом ты можеш заглючить прогу
     
    1 person likes this.
  10. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Ок, но как решить проблему, которую я описал выше?
     
  11. Hiro Protagonist

    Joined:
    26 Aug 2009
    Messages:
    132
    Likes Received:
    24
    Reputations:
    -2
    2mailbrush

    у тебя с логикой программы не в порядке. Ты смотри где интерлокед вызываешь. Глобальная переменная увеличится только пять раз, по числу создаваемых потоков, а в цикле её значение аж с 15 сравнивается.

    да и это

    PHP:
    for(i=0;i<=4;i++)
    recvsresponse1024 );
    тоже некорректно. память нужно выделять динамически и проверять возвращаемое recv значение.
     
    #11 Hiro Protagonist, 25 Sep 2009
    Last edited: 25 Sep 2009
  12. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Сейчас пороверю. Кстати, если не в цикле делать, recv не берет 5кб, а только 1кб, хотя я делаю 1024*5
     
  13. Hiro Protagonist

    Joined:
    26 Aug 2009
    Messages:
    132
    Likes Received:
    24
    Reputations:
    -2
    2mailbrush

    дык в цикле и делай, только память динамически выделяй.

    чтото типа

    int i = 0;
    char *buf = calloc(1024);

    while(recv(s, &buf, 1024, 0))
    {
    ...
    i += 1024;
    buf = realloc(buf, 1024 + i);

    }

    free(buf);

    PS и не забывай ещё обрабатывать ошибки recv
     
  14. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Поставил внутрь цикла while инкремент - первых пять потоков обрабатывают 5й айди, вторые 10, третье 15 и т.д... Эх...

    Hiro Protagonist, меня пока интересует инкремент. В любом случае спасибо всем за помощь!
     
    #14 mailbrush, 25 Sep 2009
    Last edited: 25 Sep 2009
  15. Hiro Protagonist

    Joined:
    26 Aug 2009
    Messages:
    132
    Likes Received:
    24
    Reputations:
    -2
    2mailbrush

    я вообще не понимаю зачем тебе этот цикл. Делай так. чтобы один id обрабатывал один поток и умирал.
     
  16. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Воо... Вот так я и хочу, но как? Чтобы была многпоточность...
     
  17. Ra$cal

    Ra$cal Elder - Старейшина

    Joined:
    16 Aug 2006
    Messages:
    670
    Likes Received:
    185
    Reputations:
    78
    вот вы фанаты-изобретатели-мазохисты. ну сделайте вы у функции обработки потока параметр. и скидывайте значение счетчика i. ибо без понимания, почему не канает вариант с глобальной переменной, изобретать вокруг него огород - тупо.
    или юзайте мьютексы для ожидания момента окончательного считывания значения глобальной переменной в обработчике потока.
     
  18. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Везде ты должен влезть... А если я не знаю как это? И в поиск меня посылать не надо, а если все таки хочешь послать - сначала сам найди в гугле ответ на это... Или просто помоги...
     
  19. Ra$cal

    Ra$cal Elder - Старейшина

    Joined:
    16 Aug 2006
    Messages:
    670
    Likes Received:
    185
    Reputations:
    78
    гугл "beginthread msdn" первая ссылка. скролишь пример и читаешь. правда сложно?
    если чтото не понятно - спрашивай. тока леницо не надо.
    ps: проще для понимания второй пример. если бы использовал _beginthreadex вопроса о параметрах не возникло бы.
     
    #19 Ra$cal, 25 Sep 2009
    Last edited: 25 Sep 2009
  20. Ra$cal

    Ra$cal Elder - Старейшина

    Joined:
    16 Aug 2006
    Messages:
    670
    Likes Received:
    185
    Reputations:
    78
    ну если не хочешь, чтобы помогали - не пиши на форуме. мне влом запоминать ники обиженных судьбой, неспособных юзать гугл и голову.