![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
TThread копирование файла в 2-х потоках [D7]
PacMan © (2012-05-01 20:31:00)Помогите кто-нибудь.
Нужно в 2-х потоках копировать файл.
1-й поток читает файл и пишет данные в буфер1, затем содержимое буфера 1 передает в буфер 2.
2-й поток пишет из буфера 2 в файл.
бьюсь уже давно, ни как не могу понять как работают потоки.. как 1 заставить ждать, пока не выполнится что-либо во-2м...
листинг:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, unit2, StdCtrls, SyncObjs, Gauges;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
OpenDialog1: TOpenDialog;
SaveDialog1: TSaveDialog;
Label3: TLabel;
Gauge1: TGauge;
procedure Button1Click(Sender: TObject);
procedure Edit1Click(Sender: TObject);
procedure Edit2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
CriticalSection:=TCriticalSection.Create;
stream1.Create(false);
stream2.Create(false);
end;
procedure TForm1.Edit1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
Edit1.Text := OpenDialog1.FileName;
end;
procedure TForm1.Edit2Click(Sender: TObject);
begin
if SaveDialog1.Execute then
Edit2.Text := SaveDialog1.FileName;
end;
end.
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, SyncObjs;
type
stream1 = class(TThread)
protected
public
Procedure Podgot;
Procedure ZabivBuf;
Procedure Zakon;
procedure Execute; override;
end;
stream2 = class(TThread)
protected
public
procedure Execute; override;
procedure FWrite;
end;
var
f1, f2: File; // первый и второй файл
buf1: array [1..8192] of Char; //буфер 1
buf2: array [1..8192] of Char; //буфер 2
sizefile, sizeread: Int64; //размер файла и размер прочитанного
colRead, colWrite : Integer; //прочитано и записано
fOtkuda, fKuda : String; //адреса и имена файлов
CriticalSection: TCriticalSection;
flag: Boolean;
implementation
uses unit1;
procedure stream1.Execute;
begin
Podgot;
while colRead = colWrite do
begin
if flag=false then //если буфер 2 уже пустой
begin
CriticalSection.Enter;
ZabivBuf;
end;
end;
Zakon;
end;
procedure stream2.Execute;
begin
while colRead = colWrite do
begin
if flag=True then //если буфер 2 уже пустой
begin
CriticalSection.Enter;
Fwrite;
end;
end;
end;
Procedure stream1.Podgot; // подготовка
var i:integer;
begin
{i-} //даем компилятору директиву, чтобы не отслеживал ошибки ввода-вывода:
if (Form1.Edit1.Text='') or (Form1.Edit2.Text='') then //проверяем, указаны ли файлы. если нет - выходим
begin
ShowMessage('Вы не указали данные для копирования');
Exit;
end;
//Try
AssignFile(f1, Form1.Edit1.Text); //связываем файловые переменные:
AssignFile(f2, Form1.Edit2.Text);
Reset(F1, 1); //связываем файловые переменные:
sizefile := FileSize(f1); //определяем его размер в переменную:
Form1.Label3.Caption := 'Размер файла: '+IntToStr(Round(sizefile / 8192)) + ' Кб.'; //отображаем размер файла в килобайтах:
Rewrite(f2, 1); //создаем или перезаписываем второй файл:
colRead := 0; //делаем, пока не достигнут конец исходного файла
colWrite := 0;
sizeread := 0;
Screen.Cursor := crHourGlass; //песочные часы
{i+}
end;
Procedure Stream1.ZabivBuf;
var
i:integer; //while colRead = colWrite do
begin
{i-}
BlockRead(f1, Buf1, SizeOf(Buf1), colRead);
//if colRead = 0 then break;
for I := 1 to 8192 do
Buf2[i]:=Buf1[i]; //Буфер1 в Буфер2
Form1.Gauge1.Progress := Round(100*sizeread/sizefile);
Flag:=True;
CriticalSection.Leave;
{i+}
end;
Procedure Stream1.Zakon;
begin
{i-}
Screen.Cursor := crDefault; //обычный вид курсора
//Finally
CloseFile(f1);
CloseFile(f2);
//end; //try
fOtkuda := Form1.Edit1.Text; //исправляем дату
fKuda := Form1.Edit2.Text;
if IOResult <> 0 then
Application.MessageBox('Ошибка при копировании файла!', 'Внимание!!!',
MB_OK+MB_ICONERROR)
else ShowMessage('Копирование завершено успешно');
//включаем обработчик компилятором ошибок
{i+}
End;
procedure stream2.FWrite;
var
i: Integer;
begin
BlockWrite(f2, Buf2, colRead, colWrite);
sizeread := sizeread + colRead;
Flag:=False;
CriticalSection.Leave;
end;
end.
Медвежонок Пятачок (2012-05-04 11:13:00) [1]смотря чем тыкаться.
если головой, то она первой и выйдет с другой стороны.
PacMan (2012-05-04 11:12:00) [2]Ладно, всем спасибо, буду тыкаться. Посмотрим что выйдет.
Вариант (2012-05-03 15:11:00) [3]А автору поста посоветовал бы Рихтера с Кларком почитать, "Программирование серверных приложений для Windows 2000". Там готовый код на си есть копирования файлов с использованием потоков. Правда придется разбираться и читать, и пробовать и на дельфи перетаскивать. Но зато интересно.
Вариант (2012-05-03 15:03:00) [4]
> sniknik © (03.05.12 14:35) [39]
> с чего это оно произойдет раньше? первый поток только создается
> когда у второго уже ThreadID есть, т.е. уже "крутится"
Может начать крутиться и быть остановленным системой, не дойдя до первого вызова GetMessage. В этом случае еще не будет очереди сообщений потока, тогда PostThreadNessage вернет fails. Вероятность этого не нулевая, хотя и повторяемость события не очень большая (если не использовать например отладчик для эмуляции), сам натыкался на такое, хотя первой командой в Execute потока был PeekMessage, просто поток был остановлен планировщиком. Поэтому было бы неплохо после PeekMessage сигналить в запускающий поток, что мой поток готов принимать сообщения и только потом уже разрешать слать ему сообщения..... ну или вариант по MSDN сценарию...
sniknik (2012-05-03 14:35:00) [5]>> - и будет утечка, если PostThreadMessage произойдет раньше оппозитного GetMessage...
> с чего это оно произойдет раньше? первый поток только создается когда у второго уже ThreadID есть, т.е. уже "крутится".
или имеется ввиду промежуток между стартом, и началом ожидания, там где пара команд открытия файла (если задержка), ну и что там дельфя своего добавляет?
в этом случае событие будет ждать в очереди, пока его явно из нее не выкинут. т.е. однозначно дождется цикла.
sniknik (2012-05-03 14:28:00) [6]> это же абстрактное дурацкое учебное задание ради самого задания.
+1
на понимание. которого автор якобы достиг ([28]) но почему то все одно "не получается".
sniknik (2012-05-03 14:26:00) [7]> что код [7] легко исчерпает память при быстром чтении и медленной записи
естественно. но цель примера не в "копипасте", как понял задача учебная.
а сделать счетчик/ограничитель на например 5 буферов, не проблема...
> - и будет утечка, если PostThreadMessage произойдет раньше оппозитного GetMessage...
с чего это оно произойдет раньше? первый поток только создается когда у второго уже ThreadID есть, т.е. уже "крутится".
Anatoly Podgoretsky (2012-05-03 14:23:00) [8]А может от него надо избавиться, тогда задание предстает совсем в другом свете.
Медвежонок Пятачок (2012-05-03 14:19:00) [9]что код [7] легко исчерпает память при быстром чтении и медленной записи
какая разница, если даже это реально произойдет?
это же абстрактное дурацкое учебное задание ради самого задания.
han_malign (2012-05-03 14:10:00) [10]
> что код [7] легко исчерпает память при быстром чтении и медленной записи
- и будет утечка, если PostThreadMessage произойдет раньше оппозитного GetMessage...
Sha (2012-05-03 14:01:00) [11]> sniknik © (03.05.12 12:48) [32]
сразу бросилось в глаза,
что код [7] легко исчерпает память при быстром чтении и медленной записи
sniknik (2012-05-03 12:48:00) [12]> Если уж делать по теории,
> то поток чтения занимается только чтением и ничего не знает о потоке записи,
> а поток записи занимается только записью и ничего не знает о потоке чтения.
посмотри код в [7], так и сделано.
условие завершения второго потока только забыл, 1 строка, после решил оставить это автору топика.
Sha (2012-05-03 12:23:00) [13]> han_malign (03.05.12 09:07) [24]
В данном случае пул и есть кольцевой буфер,
т.к. мы имеем дело с последовательными операциями чтения и записи.
Если уж делать по теории,
то поток чтения занимается только чтением и ничего не знает о потоке записи,
а поток записи занимается только записью и ничего не знает о потоке чтения.
Медвежонок Пятачок (2012-05-03 12:21:00) [14]стартуем два потока.
первый распределяет буфер и читает файл.
заполнив буфер, первый поток посылает средмессадж второму и сообщает адрес и длину буфера.
второй поток вынимает данные буфера в файл, затем грохает буфер и усыпает.
первый поток, закончив с одним буфером, распределяет второй и снова оповещает второй поток о готовой порции данных.
второй поток просыпается и повторяет операцию.
после того, как файл прочитан целиком, первый поток оповещает второй, что можно закругляться и идти пить пиво.
sniknik (2012-05-03 12:01:00) [15]> ))) Решение вижу, понимаю алгоритм... не понимаю как реализовать.
угадал все буквы, не смог назвать слово... это нельзя назвать "понимаю".
> В моем примере 1-й поток начинает работать, но проходит только 1 круг, из-за того что 2-й поток не хочет работать.
твой пример, это набор функций почти каждая из которых "не в том месте и не в то время".
вот что ты сделал по поводу
> и кстати у тебя куча всего чего нельзя в потоках... типа прямого обращения к vcl.
?
а ведь это может приводить к блокировкам, "локированию" потока. ... не думаю что ты "нарвался" именно на это (более вероятно кривая логика), но рассматривать код в котором такие очевидные глюки вряд ли кто будет (я точно нет).
PacMan (2012-05-03 11:13:00) [16]))) Решение вижу, понимаю алгоритм... не понимаю как реализовать.
В моем примере 1-й поток начинает работать, но проходит только 1 круг, из-за того что 2-й поток не хочет работать.
sniknik (2012-05-03 09:45:00) [17]> и вот что заимел(
заслуженная двойка по предмету... даже видя перед собой решение, не понимаешь, что это такое.
Медвежонок Пятачок (2012-05-03 09:44:00) [18]она на тебя просто запала. запишись на дополнительное индивидуальное занятие. вечернее.
PacMan (2012-05-03 09:37:00) [19]
> Anatoly Podgoretsky © (02.05.12 21:36) [20]
хотел другое задание взять... сказала: "Нет, оно не интересное, я тебе вот это дам..."
и вот что заимел(
han_malign (2012-05-03 09:07:00) [20]
> Почитай про кольцевой буфер клавиатуры
- низзя, сказано "тройная буферизация", значит так и надо делать - нормальный FIFO можно сделать в качестве факультатива...
По сути(технический перевод):
1. есть пул блоков чтения и пул блоков записи фиксированной максимальной глубины...
2. пул чтения изначально заполнен указателями на блоки буфера(скажем размера 64K)
3. поток чтения выбирает блок из пула чтения, заполняет его и помещает в пул записи.
4. поток записи выбирает блок из пула записи, обрабатывает - и помещает в пул чтения.
5. соответственно, если в пуле нет блоков, а "процесс" не завершен - поток приостанавливается.
Тупой спин, кстати - тоже не возбраняется - в некоторых случаях(когда процессоров/ядер не меньше чем потоков и нагрузка на потоки сбалансирована) он увеличивает производительность за счет меньшего количества переключения контекста потоков...
А по делу - это делается двумя счетчиками прочитанных/записанных данных - раздельный доступ к частям буфера обеспечивается счетчиками:
cbR += read(pb[cbR mod sizeof(pb)], min(MAX_BLOCK,sizeof(pb) - (cbR-cbW)));
cbW += write(pb[cbW mod sizeof(pb)], min(MAX_BLOCK,cbR-cbW));
З.Ы. Кстати - в этом варианте уже посчитан последний блок неполного размера...
З.З.Ы. И никакой тройной буферизации нет, буфер в рамках одного уровняодного тракта - один, хотя и может состоять из произвольного числа блоков(т.н. " FIFO-половинки")...