-
Помогите кто-нибудь. Нужно в 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.
-
> как 1 заставить ждать, пока не выполнится что-либо во-2м... а то что по сути это станет одним потоком, просто с разнесенным кодом/логикой на два тебя не смущает?
получается типа, "хочу нести воду в 2х ведрах, но так чтобы пол пути в одном, пол в другом. как на полпути воду в ведрах местами поменять? емкостей то больше нет".
-
меня это не смущает, т.к. по заданию мне нужно копировать файл в 2-х потоках, и препод ничего не может мне подсказать по этому поводу. Если у кого-то есть "знание" как сделать хотя бы это, буду благодарен. Если кто-то может показать как сделать оптимальнее, также буду благодарен.
-
> как 1 заставить ждать, пока не выполнится что-либо во-2м... уже используется - CriticalSection.Enter; во втором заставит первый ждать, не скажу насчет правильности, логики его использования у тебя, но это вот оно.
и кстати у тебя куча всего чего нельзя в потоках... типа прямого обращения к vcl.
-
ну синхронайз пока не трогаю... программа запускается и так.
при запуске я выбираю исходный файл, выбираю куда и под каким именем его скопировать... по кнопке "что-то происходит" и в конце выводится "копирование успешно завершено". открываю директорию назначения и вижу файлик с нужным именем... !НО! его размер 0 байт. смотрел пошаговое выполнение, у меня запускается 1-й поток, проходит круг... ииииии..... "копирование завершено".
чтобы было понятнее что и как, могу на почту скинуть прогу.
-
> sniknik © (01.05.12 20:44) [1]
там на самом деле гинеальная идея заложена :) типа пока один поток пишет первый кусок,другой читает второй и тд но она имеет смысл только при наличии 2х жестких дисков , если источник на одном а приемник на другом
-
> 1-й поток читает файл и пишет данные в буфер1, затем содержимое > буфера 1 передает в буфер 2. > 2-й поток пишет из буфера 2 в файл.
Это ты сам придумал или препод навязал тебе именно такую логику ?
-
оптимальностью не пахнет, сама логика кривая, но... более... не знаю даже как сказать, но работоспособно это точно. - unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Gauges, StdCtrls;
const
WM_WRITE = WM_USER + 101;
type
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Button1: TButton;
Gauge1: TGauge;
procedure Button1Click(Sender: TObject);
private
procedure WMWrite(var Msg: TMessage); message WM_WRITE;
public
end;
var
Form1: TForm1;
implementation
const
BufSize = 4096;
WM_BUF = WM_USER + 102;
type
TFileRead = class(TThread)
protected
F: File;
FName: string;
ThreadID2: integer;
procedure Execute; override;
public
constructor Create(ID2: integer; const Name: string);
end;
TFileWrite = class(TThread)
protected
F: File;
FName: string;
MainHandle: THandle;
procedure Execute; override;
public
constructor Create(Handle: THandle; const Name: string);
end;
constructor TFileRead.Create(ID2: integer; const Name: string);
begin
ThreadID2:= ID2;
FName := Name;
inherited Create(false);
end;
procedure TFileRead.Execute;
var
Buf: PChar;
NumRead: integer;
begin
AssignFile(F, FName);
Reset(F, 1);
repeat
GetMem(Buf, BufSize);
BlockRead(F, Buf^, BufSize, NumRead);
PostThreadMessage(ThreadID2, WM_BUF, integer(Buf), NumRead);
until (NumRead = 0);
CloseFile(F);
end;
constructor TFileWrite.Create(Handle: THandle; const Name: string);
begin
MainHandle:= Handle;
FName := Name;
inherited Create(false);
end;
procedure TFileWrite.Execute;
var
Buf: PChar;
NumWritten, Count: integer;
Msg: TMsg;
begin
AssignFile(F, FName);
Rewrite(F, 1);
Count:= 0;
while GetMessage(Msg, 0, 0, 0) do begin
DispatchMessage(Msg);
Buf := Pointer(Msg.wParam);
Count:= Count + Msg.lParam;
BlockWrite(F, Buf^, Msg.lParam, NumWritten);
FreeMem(Buf);
PostMessage(MainHandle, WM_WRITE, Count, 0);
end;
CloseFile(F);
end;
procedure TForm1.WMWrite(var Msg: TMessage);
begin
Gauge1.Progress:= Msg.WParam;
Button1.Enabled:= Gauge1.Progress = Gauge1.MaxValue;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
FileWrite: TFileWrite;
begin
with TFileStream.Create(Edit1.Text, fmOpenRead) do
try
Gauge1.MaxValue:= Size;
finally
Free;
end;
Button1.Enabled:= false;
FileWrite:= TFileWrite.Create(Handle, Edit2.Text);
TFileRead.Create(FileWrite.ThreadID, Edit1.Text);
end;
end.
-
кстати в цикле второго потока (или вообще) ошибка... он не завершается. исправь.
-
-
> Это ты сам придумал или препод навязал тебе именно такую > логику ?
на самом деле задание звучит так: "Разработать компонент, реализующий копирование файлов, при помощи тройной буферизации. Программа должна использовать потоки. Первый поток считывает блок данных из указанного файла. Второй поток записывает этот блок данных в файл. Буферизацию реализовать из 2-х массивов, состоящих из 3-х элементов. Каждый элемент - это буфер. Первый массив отвечает за чтение из файла, а второй за запись. Первый поток получает имя файла, берет пустой элемент из первого массива, записывает в него блок данных и передает второму массиву этот блок данных. При этом буфер, в котором считали блок данных, обнуляется. Второй поток, видя, что во втором массиве есть полный буфер, записывает этот буфер в файл и обнуляет его. При этом, в первом массиве выделяется новый буфер. И так пока весь файл не будет скопирован"
!НО! я даже малейшего представления не имею как это реализовать. Вот и решил упростить "слегка".
если у кого-нибудь есть идеи как это сделать, или еще лучше исходники... буду просто счастлив)) надеюсь не сильно запутал.
-
Есть коммерческое предложение, т.к. сам это все равно не напишу. Если кто желает заморочиться по заданию, договоримся о цене. Кто надумает, пишите в мыло yrungxay@mail.ru
-
> PacMan © (02.05.12 10:24) [10]
Формулировка ужасная. Это точно оригинальный текст "от препода"?
-
ага) это сфотографировано лично)
-
Имитация обработки - один поток откуда-то получает данные, складирует их в буфер. Второй поток - мониторит поступлени данных, и записывает их в файл, например, в базу данных. Вполне жизненная ситуация.
-
> PacMan © (02.05.12 10:24) [10] > при помощи тройной буферизации.
обычно говорят буферизации чего, какой операции
> Программа должна использовать потоки
ну раз надо, значит, надо
> Буферизацию реализовать из 2-х массивов, состоящих из 3-х элементов. всего 3 или 3 в каждом массиве? элементы - это что? обычно указатели
> Первый массив отвечает за чтение из файла, а второй за запись.
переменная ни за что никому не отвечает
> Первый поток получает имя файла, > берет пустой элемент из первого массива, > записывает в него блок данных..
значит, элемент - не указатель?
> и передает второму массиву этот блок данных.
передать переменной? она же памятник копирование данных или указателей? кто об этом и как узнает? а если некуда передавать? табличка "занято" 3 раза?
> При этом буфер, в котором считали блок данных, обнуляется.
нафига? если в нем были данные - то это бред может, указатель, хотя это тоже не оптимал
> Второй поток, видя, что во втором массиве есть полный буфер,
просто песня! КАК он это видит?
> записывает этот буфер в файл и обнуляет его.
опять же нафига? опять же, если видит, но не может? типа еще е все доделал
> При этом, в первом массиве выделяется новый буфер.
вроде мы не удаляли ничего, в таком случае лимит в 3 буфера будет превышен!
> Cobalt
Конечно, можно мониторить поступление телефонного звонка, но, вероятно, намного проще брать трубку по звонку.
-
> Sha (02.05.2012 12:57:15) [15]
Это тест на испытание копипастеров, не выживет так не выживет.
-
> Sha © (02.05.12 12:57) [15]
Это текст задания слово в слово. Причем сама препод не может ни чего пояснить по нему. Спрашиваю из элементов какого типа состоят массивы(массив байт или строки или еще что)? Отвечает, что как мне удобнее пусть так и будет, а про копирование вообще ни чего сказать не может. как-то так.
-
> сама
Это многое что объясняет)
-
> там на самом деле гинеальная идея заложена :) > типа пока один поток пишет первый кусок,другой читает второй > и тд > но она имеет смысл только при наличии 2х жестких дисков > , если источник на одном а приемник на другом
или если источники типа \\server\share\file.txt
-
> PacMan (02.05.2012 19:41:17) [17]
А что ты ей плохого сделал?
-
> Anatoly Podgoretsky © (02.05.12 21:36) [20]
А может просто не сделал, притом что должен был сделать ? imho, сей вариант наиболее вероятен)
-
> PacMan ©
Почитай про кольцевой буфер клавиатуры, прерывания 9 и 16, можно поверхностно. Изобрази ей нечто похожее.
-
> Sha © (03.05.12 01:20) [22]
Ностальгия?
-
> Почитай про кольцевой буфер клавиатуры
- низзя, сказано "тройная буферизация", значит так и надо делать - нормальный 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-половинки")...
-
> Anatoly Podgoretsky © (02.05.12 21:36) [20]
хотел другое задание взять... сказала: "Нет, оно не интересное, я тебе вот это дам..." и вот что заимел(
-
она на тебя просто запала. запишись на дополнительное индивидуальное занятие. вечернее.
-
> и вот что заимел( заслуженная двойка по предмету... даже видя перед собой решение, не понимаешь, что это такое.
-
))) Решение вижу, понимаю алгоритм... не понимаю как реализовать. В моем примере 1-й поток начинает работать, но проходит только 1 круг, из-за того что 2-й поток не хочет работать.
-
> ))) Решение вижу, понимаю алгоритм... не понимаю как реализовать. угадал все буквы, не смог назвать слово... это нельзя назвать "понимаю".
> В моем примере 1-й поток начинает работать, но проходит только 1 круг, из-за того что 2-й поток не хочет работать. твой пример, это набор функций почти каждая из которых "не в том месте и не в то время".
вот что ты сделал по поводу > и кстати у тебя куча всего чего нельзя в потоках... типа прямого обращения к vcl. ? а ведь это может приводить к блокировкам, "локированию" потока. ... не думаю что ты "нарвался" именно на это (более вероятно кривая логика), но рассматривать код в котором такие очевидные глюки вряд ли кто будет (я точно нет).
-
стартуем два потока. первый распределяет буфер и читает файл. заполнив буфер, первый поток посылает средмессадж второму и сообщает адрес и длину буфера. второй поток вынимает данные буфера в файл, затем грохает буфер и усыпает. первый поток, закончив с одним буфером, распределяет второй и снова оповещает второй поток о готовой порции данных. второй поток просыпается и повторяет операцию. после того, как файл прочитан целиком, первый поток оповещает второй, что можно закругляться и идти пить пиво.
-
> han_malign (03.05.12 09:07) [24]
В данном случае пул и есть кольцевой буфер, т.к. мы имеем дело с последовательными операциями чтения и записи.
Если уж делать по теории, то поток чтения занимается только чтением и ничего не знает о потоке записи, а поток записи занимается только записью и ничего не знает о потоке чтения.
-
> Если уж делать по теории, > то поток чтения занимается только чтением и ничего не знает о потоке записи, > а поток записи занимается только записью и ничего не знает о потоке чтения. посмотри код в [7], так и сделано. условие завершения второго потока только забыл, 1 строка, после решил оставить это автору топика.
-
> sniknik © (03.05.12 12:48) [32]
сразу бросилось в глаза, что код [7] легко исчерпает память при быстром чтении и медленной записи
-
> что код [7] легко исчерпает память при быстром чтении и медленной записи
- и будет утечка, если PostThreadMessage произойдет раньше оппозитного GetMessage...
-
что код [7] легко исчерпает память при быстром чтении и медленной записи
какая разница, если даже это реально произойдет? это же абстрактное дурацкое учебное задание ради самого задания.
-
А может от него надо избавиться, тогда задание предстает совсем в другом свете.
-
> что код [7] легко исчерпает память при быстром чтении и медленной записи естественно. но цель примера не в "копипасте", как понял задача учебная. а сделать счетчик/ограничитель на например 5 буферов, не проблема...
> - и будет утечка, если PostThreadMessage произойдет раньше оппозитного GetMessage... с чего это оно произойдет раньше? первый поток только создается когда у второго уже ThreadID есть, т.е. уже "крутится".
-
> это же абстрактное дурацкое учебное задание ради самого задания. +1 на понимание. которого автор якобы достиг ([28]) но почему то все одно "не получается".
-
>> - и будет утечка, если PostThreadMessage произойдет раньше оппозитного GetMessage... > с чего это оно произойдет раньше? первый поток только создается когда у второго уже ThreadID есть, т.е. уже "крутится". или имеется ввиду промежуток между стартом, и началом ожидания, там где пара команд открытия файла (если задержка), ну и что там дельфя своего добавляет? в этом случае событие будет ждать в очереди, пока его явно из нее не выкинут. т.е. однозначно дождется цикла.
-
> sniknik © (03.05.12 14:35) [39]
> с чего это оно произойдет раньше? первый поток только создается > когда у второго уже ThreadID есть, т.е. уже "крутится"
Может начать крутиться и быть остановленным системой, не дойдя до первого вызова GetMessage. В этом случае еще не будет очереди сообщений потока, тогда PostThreadNessage вернет fails. Вероятность этого не нулевая, хотя и повторяемость события не очень большая (если не использовать например отладчик для эмуляции), сам натыкался на такое, хотя первой командой в Execute потока был PeekMessage, просто поток был остановлен планировщиком. Поэтому было бы неплохо после PeekMessage сигналить в запускающий поток, что мой поток готов принимать сообщения и только потом уже разрешать слать ему сообщения..... ну или вариант по MSDN сценарию...
-
А автору поста посоветовал бы Рихтера с Кларком почитать, "Программирование серверных приложений для Windows 2000". Там готовый код на си есть копирования файлов с использованием потоков. Правда придется разбираться и читать, и пробовать и на дельфи перетаскивать. Но зато интересно.
-
Ладно, всем спасибо, буду тыкаться. Посмотрим что выйдет.
-
смотря чем тыкаться. если головой, то она первой и выйдет с другой стороны.
|