Малко помощ с TThread (Terminate, FreeOnTerminate и други приключения в сферата на нишките)

Опитвам се да постигна следното (използвайки Delphi7): След като влезе в моята програма, потребителят получава контрол, но във фонов режим отделна нишка изтегля файл от интернет, за да провери дали текущият лицензен ключ е в черния списък. Ако е така, потребителят получава подкана и програмата се прекратява.

Така че създадох отделен клас TThread, който изтегля черния списък от мрежата с помощта на InternetOpenURL/InternetReadFile.

Проблемът ми е следният:

Ако потребителят напусне моята програма, преди изтеглянето във фонов режим да приключи, нишката на мениджъра на лицензи трябва да бъде прекратена от основната нишка.

Ако нишката е свършила работата си, тя трябва да прекрати автоматично.

Ако използвам FreeOnTerminate := true, не мога да прекратя нишката от основната нишка. Но иначе, как мога да накарам нишката да освободи своите ресурси, след като е свършила работата си?

Другият ми въпрос е:

Ако лицензният ключ е в черния списък, използвам Синхронизиране, за да направя нещо с определени ресурси от основния формуляр на приложението.

Но как да разбера дали потребителят вече е затворил приложението и програмата е във FormDestroy на основния формуляр, например? Ако синхронизирам в грешното време, това може да доведе до нарушения на достъпа...

Благодаря!


person Steve    schedule 13.08.2010    source източник
comment
Просто съм любопитен... защо правите това, вместо просто да изпратите лицензионния ключ към, да речем, уеб базирано приложение и да получите отговор „да/не“, за да потвърдите ключа?   -  person GrandmasterB    schedule 14.08.2010
comment
Правя това в момента. Извиквам php скрипт с лицензния ключ като параметър и ако е в черния списък, той връща низ в ЧЕРЕН СПИСЪК и причината за черния списък (клиентът не е платил сметката и т.н.) Също така използвам същия скрипт/ част от програмата за издаване на актуализации на лиценз (потребителят подновява абонамента за софтуер)   -  person Steve    schedule 14.08.2010
comment
Просто казано, ако искате да взаимодействате с нишка по някакъв начин (това включва WaitFor или Terminate при специални условия), тогава FreeOnTerminate не е правилният инструмент за работата. FreeOnTerminate е пряк път, който използвате за запалване и забравяне. Можете да създадете TSimpleEvent за проследяване кога/дали нишката действително е унищожена и да позволите на приложението си да WaitFor това. Но тогава можете сами да унищожите нишката с: Thread.Terminate; Thread.WaitFor(<timeout>); Thread.Free;.   -  person Disillusioned    schedule 24.10.2014


Отговори (3)


Мисля, че това е, което се опитвате да направите.... Присвоете събитие OnTerminate() на TThread, което се изпълнява като част от вашата основна нишка, когато работната нишка бъде прекратена. Можете да правите всичките си актуализации на потребителския интерфейс там (черният списък). Направете свойство на вашата работна нишка като „DownloadComplete“ и правете черния списък/валидирането само ако това е зададено на true в събитието OnTerminated. Това би трябвало да позволи на нишката да се освободи независимо от състоянието на изтеглянето, когато програмата се изпълнява, стига да я .watifor() преди програмата да излезе.

person GrandmasterB    schedule 13.08.2010
comment
Но по този начин OnTerminate няма да бъде извикан преди MyThread.Execute да приключи, нали? Тъй като в MyThread.Execute изтеглям файла - и какво се случва, ако изтеглянето блокира поради неотговарящ сървър? MyThread.WaitFor ще продължи да чака, докато изтеглянето приключи и приложението увисне... (Не искам това) - person Steve; 14.08.2010
comment
Как се справяте с изтеглянето? Трябва да можете да го отмените по всяко време, като проверите прекратеното свойство на нишката. Добрата рутина за изтегляне трябва да има или таймаут, или обратно извикване, което ви позволява да се откажете от изтеглянето, ако нишката е маркирана като прекратена. - person GrandmasterB; 14.08.2010
comment
Използвам код като този: delphi.about.com/od/internetintranet/ a/get_file_net.htm (InternetOpenURL, InternetReadFile) Мисля, че тези функции имат стойност за изчакване, но не можете да ги отмените... - person Steve; 14.08.2010
comment
Никога не съм го използвал, но ако го няма, бих превключил на нещо, което позволява изчакване, ако мрежовата връзка увисне. В противен случай, нишки или не, вашият процес ще виси, докато бъде физически прекратен. Наистина, въз основа на кода, който използвате, извършването на изтегляне чрез четене на сокет не е много по-сложно. Основно същият подход - отворете връзката, изпратете http GET и започнете да четете отговора. Можете да използвате нещо като библиотеката synapse (ararat.cz/synapse/doku.php/start) за това. - person GrandmasterB; 14.08.2010
comment
Благодаря за предложението Проблемът ми със Synapse е, че изглежда остарял (няма версия на Delphi2010, Delphi2009 е само експериментален) - бих предпочел да не разчитам на компоненти на трети страни за такова просто нещо. Някакви други предложения? - person Steve; 14.08.2010
comment
Delphi има свой собствен клас на сокет (мисля, че се нарича TClientSocket или TClientWinSocket или нещо подобно), който може да се използва за него, ако искате да избегнете компонент. Или можете да използвате директно API функциите на windows socket. - person GrandmasterB; 16.08.2010
comment
Трябва да отбележа това, защото е фундаментално погрешно. Ако използвате WaitFor() на свободна при прекратяване нишка, рискувате AV, защото обектът на нишката може да бъде унищожен, преди да започнете да чакате. Единственият начин да направите това работещо е да създадете отделно събитие, което може да бъде сигнализирано в манипулатора на събития OnTerminate. След това можете спокойно да изчакате събитието и да го унищожите. В който случай можете просто да не правите нишката си FreeOnTerminate и да я освободите сами, когато е завършена. - person Disillusioned; 24.10.2014

Първо, във вашия обект за проверка на нишка, създайте флаг "завършен". Можете да проверите дали това е вярно, за да определите дали всичко е наред. Както предлага Chris T, накарайте нишката да зададе глобална стойност, за да покаже, че нещата са добри/лоши, така че основната нишка да може да използва нещо като таймер, за да провери това и че всичко е наред или да предприеме подходящи действия.

След това, ако приложението ви иска да се откаже по-рано, обадете се

  MyThread.Terminate;
  MyThread.WaitFor;

И в нишката проверете дали Terminated е зададен в подходящи точки. По този начин можете да затворите добре.

person mj2008    schedule 13.08.2010
comment
Знам за WaitFor, но ако нишката завърши работата си преди програмата да бъде затворена, как може да освободи своите ресурси (низове, които използва например), без да използва флага FreeOnTerminate? - person Steve; 13.08.2010

За втория въпрос, не си правете труда да актуализирате потребителския интерфейс от нишката. Просто задайте глобален флаг. т.е. IsBlackListed := true; Сега можете да използвате този глобален флаг като основа за заяждане на потребителя в отговор на някакво действие, инициирано от потребителя. Като OnFileSave... ShowMessage('Бих искал да запазя този файл за вас, но за съжаление използвате ключ от черния списък.');

IsBlackListed Redux....

// globally...
var
  IsBlackListed  : integer;

...
initialization
  IsBlackListed  := 0;

...
// in your thread:
if OhMyGodThisIsABlackListedKey then
  IsBlackListed := 1;

... Back in the main code, maybe in the OnSaveMyData event:

if IsBlackListed then
begin
  IsBlackListed := -1; // so we don't repeat ourselves
  MessageBox('Hey, you naughty pirate!');
  MyDBConnection.Whatever.DoSQL('insert into licensetable (name,key) values(pirate,blacklisted);
end;
person Chris Thornton    schedule 13.08.2010
comment
Здравейте! Ако ключът е в черния списък, трябва да актуализирам запис в базата данни на програмата и компонентите на DB са в главния формуляр - следователно трябва да се актуализира преди FormDestroy. Как да направя това? - person Steve; 13.08.2010
comment
Пропуснете компонентите на DB и вмъкнете в черния списък (потребителско име, ключ, бла-бла) стойности (яда, дяда); - person Chris Thornton; 13.08.2010
comment
Вече го наричам така: MainForm.IBCQuery.SQL.Text := 'INSERT INTO ...'; Не мога да създам компонента IBCQuery динамично, тъй като не искам да правя множество връзки към базата данни. (и IBCConnection също е в MainForm) - person Steve; 14.08.2010
comment
Тогава просто го пропуснете и го направете следващия път, когато не са в средата на спиране. Ще добавя някакъв код към моя отговор, за да се справя с това.... - person Chris Thornton; 14.08.2010
comment
Благодаря за кода, но проблемът е, че веднага щом бъде открит ключ от черния списък, той МОМЕНТАЛНО трябва да задейства съобщение за показване, да запази нещо в db и след това да затвори приложението. За това мога да използвам Синхронизиране от моята нишка. Проблемът е, че ако приложението се затваря и основната нишка е във FormDestroy, извършването на синхронизиране ще доведе до нарушения на достъпа... И така, как да разбера от моята нишка, че е безопасно да направя синхронизиране? - person Steve; 14.08.2010
comment
@Steve - Сигурен съм, че имате своите причини, но бих предложил да се захванете с всичко погрешно. Явно не си съгласен, разбирам. Между другото, и FWIW, направих много хиляди $$$ чрез откриване на ключове в черния списък, предприемане на малко по-мек подход и превръщане на пиратите в продажби. Но съм сигурен, че и твоят начин е добър. - person Chris Thornton; 14.08.2010
comment
Крис, това не е софтуер за споделяне. Това е корпоративен софтуер. Те плащат на годишна база, така че им издавам годишни лицензи. Но ако даден потребител не плаща навреме или изобщо не плаща, имам нужда от начин да деактивирам достъпа му в рамките на едногодишния интервал. Не искам да ги заяждам, просто забранете достъпа до цялата програма. - person Steve; 14.08.2010