Копирование файла в потоке.
Задумал я в потоке файл копировать, сделал, как в книжках пишут, а у меня при запуске потока прога виснет, но поток выполняется:
__fastcall TFileThread::TFileThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
FreeOnTerminate = True;
}
//---------------------------------------------------------------------------
void __fastcall TFileThread::Execute()
{
// Объявление переменных
AnsiString InFilePath, OutFilePath;
const size_t BufferSize = 64;
size_t Readed;
char DataBuffer[BufferSize];
long FileSize, SymCount = 0;
// Входной / выходной файл
InFilePath = "\\\\abc-01\\t\1.txt";
OutFilePath = "C:\\1.txt";
// Входной / выходной файл
ifstream In(InFilePath.c_str(), ios::in | ios::binary);
ofstream Out(OutFilePath.c_str(), ios::out | ios::binary);
// Проверка на доступность к файлам
if(!In || !Out)
return;
// Определение размера файла
In.seekg(0, ios::end);
FileSize = In.tellg();
In.seekg(0, ios::beg);
// Сохранение файла из in в out
while(In)
{
// Считывание данных в буфер
In.read(DataBuffer, BufferSize);
Readed = In.gcount();
if(Readed > 0)
Out.write(DataBuffer, Readed);
// Подсчет кол-ва считанных символов
SymCount += Readed;
// Изменение позиции прогрессбара
Pos = SymCount * 100 / FileSize;
Synchronize(UpdateProgress);
}
// Закрытие файлов
In.close();
Out.close();
}
//---------------------------------------------------------------------------
void __fastcall TFileThread::UpdateProgress()
{
Form1->ProgressBar->Position = Pos;
}
//---------------------------------------------------------------------------
а вот так я его запускаю:
File->Resume();
скажите чего не дочитал я?
ofstream ofile("...");
ofile << ifile;
ifile.close();
ofile.close();
Немного не в тему, но все же. Почему нельзя скопировать так:
ofstream ofile("...");
ofile << ifile;
ifile.close();
ofile.close();
Вот несколько весомых доводов:
1. Если копировать твоим способом по локальной сети (а у меня именно так), то прога подвисает, до окончания процесса копирования.
2. Если считывать построчно, а потом сохранять (тут еще один момент - индикатор копирования для пользователя), а в перерывах ставить ProcessMessage (что не очень красиво), то это не совсем правильно.
По-моему потоки и предназначены для длительных, законченных операций, или я не прав?
А как мою задачку решить?
Доброе утро!
Задумал я в потоке файл копировать, сделал, как в книжках пишут, а у меня при запуске потока прога виснет, но поток выполняется:
...
скажите чего не дочитал я?
не силен я в STL, но эта проверка окночания цикла смущает
{
тем более, что дальше по тексту идет обращение к
In.close();
не силен я в STL, но эта проверка окночания цикла смущает
{
Ну ясно дело д.б. while(!In->eof())
2. Не выставлен диапазон у ProgressBar-a
3. Нахер тебе этот STL сдался используй TFileStraem, API (не люблю я ее)
не силен я в STL, но эта проверка окночания цикла смущает
{
тем более, что дальше по тексту идет обращение к
In.close();
В STL я тоже не шибко, но по-моему все нормально, опирует проверял :)
Но здесь дело не в том как копируется, а где (или даже в чем) копируется. Мне кажется вся проблема в синхронизации потока, но что сделать ума не приложу?
вот если убрать:
и вставить:
то все работает как надо, но это не правильно, т.к. поток обращается к форме. Пробовал сделать приоритет низким не помогает.
Что сделать?
P.S. Млин у меня еще поток с часами тоже висит :) во дела. Никогда с потоками дело не имел.
не силен я в STL, но эта проверка окночания цикла смущает
{
тем более, что дальше по тексту идет обращение к
In.close();
Форма проверки завершения потока вполне идиоматична. Здесь используется объект файлового потока, а не указатель на него. И STL тут каким боком? :)
Попытайся для начала увеличить буфер - 64 байта - слишком мало - хотя бы 512. Возможно, с этим тормоза и связаны. Кроме того, синхронизируй данные напрямую.
Synchronize waits for the main VCL thread to enter the message loop and then executes the passed method
//-----------------------------------------
TApplication::ProcessMessages
Interrupts the execution of an application so that Windows can process the message queue.
Вдумайся в эти строки.
--- вот именно... Дай бедняге отдохнуть. Твой поток только и делает, что крутится и крутится, поэтому занимает бОльшую часть процессорного времени, поэтому и прожка тормозит. И поэтому вызов ProcessMessages и приводит прогу в чувства. Поэтому и
--- прога как попадает в цикл, так, пока все не скопируется, вертится там без остановок -> поток сжирает все процессорное время и прога виснет, т.к. кроме копирования никаким потокам места не остается. Короче, как я уже писал тебе в другой теме --- Sleep(1) - самое простое решение --- поток "останавливается" на указанное в аргументе кол-во микросекунд и сис-ма может передать управление к-нибудь другому потоку, если есть претенденты. 1 мкс сис-ме вполне достаточно, чтобы дать поработать другим потокам. Торможения практически не заметишь.
И STL тут каким боком? :)
или я туплю или давно не программировал...
или я туплю или давно не программировал...
Стандартный потоковый ввод-вывод(вначале stdio, затем iostream class library) не одно и тоже что стандартная библиотека шаблонов. :) тебя запутало скорее всего STD и STL, кроме того, шаблоном на основе потоков часто любят иллюстрировать область применения STL. Но в принципе это не критично, и возможно на сегодняшний день библиотека более интенсивно использует STL.
// ....
// ....
Synchronize(UpdatePosition);
Sleep(1);
и стало гораздо лучше. Но является ли данное решения правильным?
Вообщем сделал вот так:
// ....
// ....
Synchronize(UpdatePosition);
Sleep(1);
и стало гораздо лучше. Но является ли данное решения правильным?
Нет решений правильных или не правильных - есть уместные в данной конкретной ситуации. Если в данной задаче это решает проблему и не создаст новых - это правильно. Скорее всего, возникает конфликт доступа между двумя потоками - и решить ее можно двумя способами - первый слипом, или же необходимо писать потокобезопасный код. Это более правильно - если это оправдано.
Нет решений правильных или не правильных - есть уместные в данной конкретной ситуации. Если в данной задаче это решает проблему и не создаст новых - это правильно. Скорее всего, возникает конфликт доступа между двумя потоками - и решить ее можно двумя способами - первый слипом, или же необходимо писать потокобезопасный код. Это более правильно - если это оправдано.
Мда что-то действительно конфликтик в потоках, а как решить?
Слип это что такое?
Мда что-то действительно конфликтик в потоках, а как решить?
Слип это что такое?
Sleep() приостанавливает выполнение на потока не определенное время, в этот промежуток времени потоку не выделяется процессорное время
Мда что-то действительно конфликтик в потоках, а как решить?
Слип это что такое?
Есть семафоры, мьютексы, объекты событий, функции ожидания - это инструменты, которые можно использовать при разрешении конфликтов потоков. В твоем случае - это возможно и не надо (точнее может здорово усложнить задачу) - вполне достаточно приостановить поток который занимает чрезмерно ресурсы процессора. Примеры работы с потоками можно посмотреть в папке $(BCB)\Examples\App\Threads.
Sleep() приостанавливает выполнение на потока не определенное время, в этот промежуток времени потоку не выделяется процессорное время
почему рекомендуют использовать Sleep(), а не ProcessMessage()?
File->FreeOnTerminate = false;
File->Resume();
File->WaitFor();
delete File;
программка в таком случае ждет завершения потока, но опять все висит.
А если вот так:
File->FreeOnTerminate = true;
File->Resume();
то соответственно не ждет :( , но и не висит.
Хотя в обоих случаях в методе Execute() стоит Sleep(1).
А задача состоит в следующем: необходимо, чтобы запускался поток, а программа ждала его завершения, при этом не подвисая.
Подскажите как решить проблему?
А задача состоит в следующем: необходимо, чтобы запускался поток, а программа ждала его завершения, при этом не подвисая.
Подскажите как решить проблему?
Так не получится, подождать завершения потока можно передав ф-ии WaitForSingleObject() хендл потока она будет ждать когда поток завершится и только после этого вернет управление в вызывающий поток (подозреваю, что тоже делает File->WaitFor();). Все это время вызывающий (у тебя это основной) поток будет "спать" (и никакой обработки цикла сообщений не будет). Чего хочешь получить в итоге, сформулируй задачу еще раз.
Чего хочешь получить в итоге, сформулируй задачу еще раз.
Ну я же вроде написал :):
Но не вопрос, скажу по-другому:
я хочу чтобы по некоторому событию у меня копировался файл в потоке, но при этом программа не подвисала и ждала завершения потока. Вот как это реализовать?
Скажем по событию кнопки, происходит следующее:
File->FreeOnTerminate = false;
File->Resume();
File->WaitFor();
delete File;
// апосля дальше некоторый код
// который ждал бы завершения потока
.....
// тут еще один поток
// опять необходимо ожидание завершения
Но не вопрос, скажу по-другому:
я хочу чтобы по некоторому событию у меня копировался файл в потоке, но при этом программа не подвисала и ждала завершения потока. Вот как это реализовать?
[/code]
Никак (или я тебя не совсем понимаю). Уже говорил если пустить поток и ждать его завершения, как ты хочешь, то основной поток при этом работать не будет (соответственно не идет обработка цикла сообщений т.е. программа подвисает). Можно пустить поток для копирования файла и при завершении копирования отправить основному потоку сообщение, а?
Никак (или я тебя не совсем понимаю). Уже говорил если пустить поток и ждать его завершения, как ты хочешь, то основной поток при этом работать не будет (соответственно не идет обработка цикла сообщений т.е. программа подвисает). Можно пустить поток для копирования файла и при завершении копирования отправить основному потоку сообщение, а?
:(
Мне нужно чтобы один поток копировал файл, потом прога парсила файл, а потом эти данные передавались во второй поток, где будут обрабатываться! Как такую задачу решить?
Можно по-другому сделать: тот код, который должен ожидать завершения файлового потока, выполнять в отдельном (уже 3ем) потоке, тогда тормозить будешь этот 3й поток, а основной будет жить.
Или еще так: помести "ожидающий" код, в файловый поток, чтобы он выполнился после завершения копирования файла (куда-нибудь в конец Execute) --- и программа не повиснет и ждать будет.
Вот какую логику преследую (может не совсем уместная):
1. Часики - отдельный поток, чтоб их так, всегда ходили, не подтормаживали (хотя в данной ситуации уж незнаю как и быть)
2. Копирование файла - отдельный поток, тут копируется файл по сети, файл большой, скорость по сетке никакая (dialup)
3. Парсинг файла - основной поток (сама программа)
4. Обработка полученного файла - отдельный поток, длительные вычисления.
Хочется увидеть "на выходе":
Первый поток всегда работает, т.е. всю "жизнь" программы, по клике на кнопке запускается второй поток, копируется файлик, по окончании копирования, парсится, потом полученные данные передаются в 4 поток и обрабатываются! Во как.
Как я понял одно из решений затолкать 2 поток, парсинг файла и 4 поток в новый поток и там все делать?
млин ерунда какая-то!
Вот какую логику преследую (может не совсем уместная):
1. Часики - отдельный поток, чтоб их так, всегда ходили, не подтормаживали (хотя в данной ситуации уж незнаю как и быть)
2. Копирование файла - отдельный поток, тут копируется файл по сети, файл большой, скорость по сетке никакая (dialup)
3. Парсинг файла - основной поток (сама программа)
4. Обработка полученного файла - отдельный поток, длительные вычисления.
Хочется увидеть "на выходе":
Первый поток всегда работает, т.е. всю "жизнь" программы, по клике на кнопке запускается второй поток, копируется файлик, по окончании копирования, парсится, потом полученные данные передаются в 4 поток и обрабатываются! Во как.
Как я понял одно из решений затолкать 2 поток, парсинг файла и 4 поток в новый поток и там все делать?
Мне кажется, что столько потоков совершенно ни к чему
Мне кажется, что столько потоков совершенно ни к чему
А как тогда решать задачу?
До этого времени у меня все было реализовано в основном потоке программы, но тогда появляются мелкие тормоза, проблемы с интерфейсом, когда файлик по сети копируется и т.п..
Когда идет обработка данных, еще хуже, для примера нажимаешь на заголовок окна программы и все вычисления подвисают, естественно если обработка загнана в поток такого не наблюдается, вот и решил так сделать.
Вот какую логику преследую (может не совсем уместная):
1. Часики - отдельный поток, чтоб их так, всегда ходили, не подтормаживали (хотя в данной ситуации уж незнаю как и быть)
2. Копирование файла - отдельный поток, тут копируется файл по сети, файл большой, скорость по сетке никакая (dialup)
3. Парсинг файла - основной поток (сама программа)
4. Обработка полученного файла - отдельный поток, длительные вычисления.
Вот как бы надо это зделать используя только один поток и еще 2 по необходимости.
Во время бездействия будет только один поток.
1 имеем 1ый поток - основной для интерфейса и часов
2. Для часов отдельный поток не нужен,будем в 1ом потке отлавливать WM_TIMER и сменять время
3. Когда необходимо копирование файла создаем ждущий поток(2й поток) отсюда создаем 3й поток
и во втором ждем завершения операции копирования 3его потока с помощью WaitForSingleObject
4. Когда 3ий поток завершает копирование и уничтожается мы это отлавливаем по тому что вернул
WaitForSingleObject и запускаем снова 3ий поток но уже для вычислительных операций и снова ждем из второго. попутно можем из 2го потока выдавать какие нить уведомления пользователю. После того как вычисления завершились,3й поток уничтожается,2й поток делает завершающие операции(уведомляет пользователя) и сам завершается.
5. Теперь у нас снова токо 1 поток. Если необходимо получить файл возвращяемся к пункту 3...
Вот как бы надо это зделать используя только один поток и еще 2 по необходимости.
Во время бездействия будет только один поток.
1 имеем 1ый поток - основной для интерфейса и часов
2. Для часов отдельный поток не нужен,будем в 1ом потке отлавливать WM_TIMER и сменять время
3. Когда необходимо копирование файла создаем ждущий поток(2й поток) отсюда создаем 3й поток
и во втором ждем завершения операции копирования 3его потока с помощью WaitForSingleObject
4. Когда 3ий поток завершает копирование и уничтожается мы это отлавливаем по тому что вернул
WaitForSingleObject и запускаем снова 3ий поток но уже для вычислительных операций и снова ждем из второго. попутно можем из 2го потока выдавать какие нить уведомления пользователю. После того как вычисления завершились,3й поток уничтожается,2й поток делает завершающие операции(уведомляет пользователя) и сам завершается.
5. Теперь у нас снова токо 1 поток. Если необходимо получить файл возвращяемся к пункту 3...
интересно.
можешь простенькую реализацию накидать, если не сложно:
1. С часами, я по WM_TIMER никогда не делал, и с сообщениями тоже плохо знаком.
2. С потоками.
заранее благодарен.
Вот что я нашел на rsdn:
if (InterlockedExchange (&nLock, 1))
return;
File = new TFileThread(true);
File->Resume();
Wait(File);
InterlockedExchange (&nLock, 0);
и реализация функции Wait:
{
DWORD nHandleCount = 1; //we wait only on hEvent
while (TRUE)
{
DWORD nResult;
MSG msg;
//dispatch message from queue
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
return;
DispatchMessage(&msg);
}
//wait for event or message in queue
nResult = MsgWaitForMultipleObjects(nHandleCount,
&hEvent,
FALSE,
INFINITE,
QS_PAINT|QS_POSTMESSAGE);
if (nResult < (WAIT_OBJECT_0 + 1))
break; //event is signalled
//otherwise msg arrived
//just continue to loop
}
}
правда после выполнения потока функция Wait() не заканчивает свою работу, что конечно хотелось бы, я так полагаю ждет сообщения. Какого подскажите?
А так все потоки живут нормально и интерфейс прорисовывается и программа ждет окончания потока, только дождаться не может :)
правда после выполнения потока функция Wait() не заканчивает свою работу, что конечно хотелось бы, я так полагаю ждет сообщения. Какого подскажите?
А так все потоки живут нормально и интерфейс прорисовывается и программа ждет окончания потока, только дождаться не может :)
Все зависит от сложности задачи - если задача - часы и копирование(по завершении парсинг) - вполне достаточно 2-х классов потока - один копирует файл, по завершении посылает сообщение окну приложения. После этого стартует второй объект класса потока - парсит - посылает уведомление и т.д. У меня так работает программа которая копирует файлы с фтп и производит их занесение в базу(правда у меня помещение в базу происходит в основном потоке программы - если захочешь переделаешь). Может это то что тебе необходимо?
Все зависит от сложности задачи - если задача - часы и копирование(по завершении парсинг) - вполне достаточно 2-х классов потока - один копирует файл, по завершении посылает сообщение окну приложения. После этого стартует второй объект класса потока - парсит - посылает уведомление и т.д. У меня так работает программа которая копирует файлы с фтп и производит их занесение в базу(правда у меня помещение в базу происходит в основном потоке программы - если захочешь переделаешь). Может это то что тебе необходимо?
можешь исходники кинуть?
{
if (msg.message == WM_QUIT)
return;
DispatchMessage(&msg);
}
подскажите как сделать чтобы данная конструкция получала, чего ожидает, т.е. выходила? Какое ей сообщение нужно и как его послать или как сделать чтобы она его получила?
можешь исходники кинуть?
Держи. Фрегмент кода который запускает поток:
{
if(GlobalClose){
CloseQuery();
return;
}
lbCurrent->Caption = "Received files from FTP";
tmLoad->Enabled = false;
lbMin->Caption = IntToStr(MinutesBetween(StrToDateTime(lbStart->Caption),Now()));
TreadFTPReceiv(FTPServer,FTPDirIn,HostUser,HostPass,WorkDir);
Application->ProcessMessages();
}
фрагмент который обрабатывает сообщения:
if(Msg.Msg==WM_FILERECEIV){
tmLoad->Enabled = false;
dmLoad = new TdmLoad(0);
lbCurrent->Caption = "Connect to DB";
dmLoad->adoConnect->Connected = false;
WideString Connect = "Provider=SQLOLEDB.1;Password="+WideString(Pass)+";Persist Security Info=True;User ID="+WideString(User)+";Initial Catalog=credcom;Data Source="+WideString(Server)+";Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=M30X;Use Encryption for Data=False;Tag with column collation when possible=False";
dmLoad->adoConnect->ConnectionString = Connect;
dmLoad->adoConnect->Connected = true;
lbCurrent->Caption = "Send data to DB";
LoadPackets();
lbCurrent->Caption = "Wait connection";
dmLoad->adoConnect->Connected = false;
delete dmLoad;
}
else if(Msg.Msg==WM_START_TIMERS){
tmLoad->Enabled = true;
lbCurrent->Caption = "Wait connection";
}
}
/*
*Загрузка пакетов в базу
*Функция проверяет директорию, указанную
*в качестве рабочей
*/
bool TfmMain::LoadPackets()
{
String tmpFind = WorkDir+"*.*";
TSearchRec sr;
try{
if(FindFirst(tmpFind,faAnyFile, sr) == 0){
do
{
if(sr.Attr&faDirectory){
;
}
else{
SendDataToBase(sr.Name);
}
}while(FindNext(sr)==0);
}
}
__finally{
FindClose(sr);
SendMessage(Handle,WM_START_TIMERS,NULL,NULL);
}
return true;
}
с остальным я думаю разберешься.
правда после выполнения потока функция Wait() не заканчивает свою работу, что конечно хотелось бы, я так полагаю ждет сообщения. Какого подскажите?
Дык черным по английски написано, что ждет - WM_QUIT от PostThreadMessage:)
Только зачем тебе такая организация потока. Сделай нормально, что бы поток копировал файл и усе. А если главному потоку(кот. рулит MainForm) надо знать, что копирование завершено, то пошли в главную форму свое сообщение.
Дык черным по английски написано, что ждет - WM_QUIT от PostThreadMessage:)
Только зачем тебе такая организация потока. Сделай нормально, что бы поток копировал файл и усе. А если главному потоку(кот. рулит MainForm) надо знать, что копирование завершено, то пошли в главную форму свое сообщение.
Да я еще вчера понял какого сообщения ждет, не надо быть гуру, чтобы понять, и понял чем его посылать только вот не получилось у меня, может устал, а может потому что никогда с сообщениями дело не имел.
Я с превеликим удовольствием сделаю именно так как ты говоришь, только вот с сообщениями я не очень.
Будь так добр напиши пример, а я уже дальше начну смотреть. Спасибо.
Да я еще вчера понял какого сообщения ждет, не надо быть гуру, чтобы понять, и понял чем его посылать только вот не получилось у меня, может устал, а может потому что никогда с сообщениями дело не имел.
Я с превеликим удовольствием сделаю именно так как ты говоришь, только вот с сообщениями я не очень.
Будь так добр напиши пример, а я уже дальше начну смотреть. Спасибо.
Вот типа этого, проверки поставишь сам.
вот решение на котором я остановился:
создание потока и его запуск, запуск ожидающей функции:
File = new TFileThread(true);
// Запуск потока
File->Resume();
// Ожидание потока
Wait((void *)File->Handle);
функция ожидания (была найдена на rsdn.ru, автор ссылается на MSDN):
{
DWORD dwRet;
MSG msg;
while(1)
{
dwRet = MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT);
if (dwRet == WAIT_OBJECT_0)
return; // The event was signaled
if (dwRet != WAIT_OBJECT_0 + 1)
break; // Something else happened
// There is one or more window message available. Dispatch them
while(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
return; // Event is now signaled.
}
}
}
Спасибо всем кто принял участие в дискуссии!!!
вот решение на котором я остановился:
функция ожидания (была найдена на rsdn.ru, автор ссылается на MSDN):
Мне все же кажется что использование функции ожидания в данном случае слишком расточительно. Ведь потоки по сути не конкурируют за ресурсы - выполнение идет последовательно. ИМХО
Мне все же кажется что использование функции ожидания в данном случае слишком расточительно. Ведь потоки по сути не конкурируют за ресурсы - выполнение идет последовательно. ИМХО
ну а что делать?!
ну а что делать?!
??? Ну а чем отправка и обработка сообщений от потоков не дело? Как я указал выше - это лично мое мнение - и мне интересно услышать обоснование другого мнения - в данной задаче (в объеме приведенном тобой) дествия должны выполнятся строго последовательно и конкуренции за ресурсы не предполагается - нужно ли использовать при этом функции привилигированного доступа? Чем хуже такой например вариант:
while(In)
{
// Считывание данных в буфер
In.read(DataBuffer, BufferSize);
Readed = In.gcount();
if(Readed > 0)
Out.write(DataBuffer, Readed);
// Подсчет кол-ва считанных символов
SymCount += Readed;
// Изменение позиции прогрессбара
// Pos = SymCount * 100 / FileSize;
SendMessage(WM_SOMEMESSAGEPAINT,SymCount,FileSize);
}
// Закрытие файлов
In.close();
Out.close();
SendMessage(WM_MYMESSAGEOK,SymCount,FileSize);
}
и всех делов? Ну естественно рабочая реализация должна быть сложнее - надо учесть ошибки чтения и пр. Но просто мне непонятно - нафига тебе ждать ЗАВЕРШЕНИЯ потока???? А если файл не прочтен? А если сеть лягла? Тебе нужно только УВЕДОМЛЕНИЕ о благополучно выполненной операции - причем по времени это не критично - ты же читаешь файл а не данные реального режима? Четы паришься? Определи сообщения об ошибках и сбоях и сообщение о нормальном завершении операции - и все. Зачем тебе дергать систему и переводить ее раз за разом в режим ядра, даже не зная будет ли от этого толк? :)
З.Ы. Кстати со Светлым Воскресением всех форумчан.Через 2 часа провозгласят: "Христос Воскресе". :)