Ребят вот не могу разобраться с такой фишкой, переодически пишу спамеры для вк, но иногда бывают проблемы (спамер, не всем рассылает, когда я изпользую многопоточное приложение) Для органицации потоков и синхронизации с аккаунтами, написал вот такой обработчик где SL = список аккантов Counter = текущий индекс в списке SL ToSpamSL = кому слать (список) IndToSpam = индекс в списке кому рассылать Code: procedure TMyThread.Execute; var Str, Save_Str, Sender, Msg, Link: string; Info: TMyInfo; Client: TMP3Rnd; IsLogin: boolean; Login, pass: string; IsMention: boolean; begin InterlockedIncrement(threads_alive); try while (not Self.Terminated) do begin // cs_enter CS_Thr.Enter; InterlockedIncrement(IndToSpam); if Counter >= SL.Count - 1 then Counter:=0 else InterlockedIncrement(Counter); //Inc(Counter); CS_Thr.Leave; // cs_leave // парсим login, pass Str:=SL.Strings[Counter]; if Str='' then continue; data:=str; Save_Str:= data; // для отладки Login:=copy(Save_Str,0,pos(ChrSep,Save_Str)-1); pass:=copy(Save_Str,pos(ChrSep,Save_Str)+1,length(Save_Str)-pos(ChrSep,Save_Str)); // ---- Str:= ToSpamSL.Strings[IndToSpam]; if Str='' then continue; //Form1.LogMemo.Lines.Add('авторизация ... '+Login); Msg := ''; //Rnd_StrWithList(RndMsgSL); data:=Login + ' '+ Str + ' '+ ' ' + Msg ; ID:=Str; // .. //CS_Thr.Enter; Client:= TMP3Rnd.Create; IsLogin := Client.Login(Login,Pass,'',''); if IsLogin then begin inc(Valid); //Form1.LogMemo.Lines.Add('авторизация ... ok'); Info := Client.GetMyInfo(Str); ISMention:=false; if Form1.CheckBox4.Checked then msg := Rnd_StrWithList(RndMsgSL) else begin msg := Form1.Memo3.Text; if Form1.CheckBox1.Checked then msg:=msg+#13#10#13#10+'['+Str+']'; // дописываем to_id end; IsMention:=Client.PostWall(Info.WallHash, '', info.ID, '', msg,'','','',''); if IsMention then begin ISSend.Add(Str); inc(CntSend) // успешно end else begin //NotSendingSL.Add(Str); inc(NotSend); end; end else inc(NotValid); Client.Free; // .... //CS_Thr.Leave; Synchronize(UpdateGUI); sleep( MyRandomRange(SleepTo, SleepDo) ); //end; //if end; finally //Synchronize(UpdateGUIDec); InterlockedDecrement(threads_alive); end; end; Вроде бы все хорошо, но если я изпользую не 1 поток, а 5 к примеру, то спамер может разослать не всем (т.е. 50/50), в чем может быть причина? Организация потока и синхронизации со список аккаунтов и списком рассылки верная? подскажите.. P.S: иcпользую Indy (Delphi 7)
у вас в одной функции смешана синхронизация и работа с гуи разделите понимать код станет намного проще и еще зачем вы юзаете interlocked внутри critical section ?
под работой с GUI вы подразуемеваете Code: Form1.CheckBox4.Checked, Form1.Memo3.Text ? Но просто читать вроде бы атрибуты компонента можно. Работа с ГУИ конкретно со статистикой у меня четко разделено, вот синхронизация: Code: Synchronize(UpdateGUI); Для того чтобы не было, что один аккаунт с одного потока, юзается в другом, с таким же индексом. Т.е. без такой конструкции, было такое (не знаю с чем это связано) Поток 1 Аккаунт 1 Сообщение Поток 2 Аккаунт 1 Сообщение Поток 3 Аккаунт 1 Сообщение ... Поток 1 Аккаунт 2 Сообщение Поток 2 Аккаунт 2 Сообщение [Такое я наблюдал на этапе отладки] Изпользуя interlocked внутри critical section (или Inc, кстати особой разницы я не заметил) , каждый поток юзает свой аккаунт параметр Count, отражает индекс элемента в списке Поток 1 Аккаунт 1 Сообщение Поток 2 Аккаунт 2 Сообщение Поток 3 Аккаунт 3 Сообщение
а где обработка исключений? если возникнет експшн внутри Execute то поток завершится как вариант - отлавливай событие OnTerminate у потока, в нем проверяй все ли отправлено, если нет то запускай новый поток (да, быдлокодерски, но в твоем случае пойдет, ибо в твой код трудно вникнуть)
Code: // cs_enter CS_Thr.Enter; InterlockedIncrement(IndToSpam); if Counter >= SL.Count - 1 then Counter:=0 else InterlockedIncrement(Counter); //Inc(Counter); CS_Thr.Leave; // cs_leave // парсим login, pass Str:=SL.Strings[Counter]; if Str='' then continue; data:=str; Save_Str:= data; // для отладки Login:=copy(Save_Str,0,pos(ChrSep,Save_Str)-1); pass:=copy(Save_Str,pos(ChrSep,Save_Str)+1,length( Save_Str)-pos(ChrSep,Save_Str)); // ---- Str:= ToSpamSL.Strings[IndToSpam]; if Str='' then continue; вот строчки интересные а что если первый поток 1) сделает InterlockedIncrement(Counter); потом выйдет CS_Thr.Leave; и его вытеснит планировщик потом войдет второй поток 2) сделает InterlockedIncrement(Counter); выйдет CS_Thr.Leave; они оба окажутся в этой точке Code: --->>> Str:=SL.Strings[Counter]; if Str='' then continue; data:=str; Save_Str:= data; // для отладки Login:=copy(Save_Str,0,pos(ChrSep,Save_Str)-1); pass:=copy(Save_Str,pos(ChrSep,Save_Str)+1,length( Save_Str)-pos(ChrSep,Save_Str)); // ---- Str:= ToSpamSL.Strings[IndToSpam]; if Str='' then continue; и у них у обоих одитнаковые индексы или так задумано ? судя по этому коду interlocked не нужен внутри critical section можно проосто inc(Counter) но вытаскивать то на что указывают индексы надо в критической секции всю работу не относящуюся к синхронизации убрать в отдельную функции за этой шелухой теряется сама синхронизация
Данная проблема наблюдалась и при inc(Counter), думал потоко-безопасное увелечение InterlockedIncrement(Counter), решит проблему, но этого не произошло. Хм, т.е. строки на акки и сообщения, в крит секции? Хорошо попробую. Поясните подробней, желательно с примером как это детально реализовать?
ну вот как нить так чтоб выглядел главный цикл ничего не относящегося к синхронизации и условию завершения Code: ... thread ... { while (!terminated) { lock(); stuff_t local_stuff = unpack_next_stuff(); unlock(); do_something(local_stuff); } }
иль на сишке можно даже так Code: ... thread ... { switch (need_next) { do { do_something(local_stuff); case need_next: lock(); stuff_t local_stuff = unpack_next_stuff(); unlock(); } while (!terminated && !zero_stuff); } }