Форум: "Начинающим";
Текущий архив: 2013.03.22;
Скачать: [xml.tar.bz2];
ВнизTThread копирование файла в 2-х потоках Найти похожие ветки
← →
PacMan © (2012-05-01 20:31) [0]Помогите кто-нибудь.
Нужно в 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.
← →
sniknik © (2012-05-01 20:44) [1]> как 1 заставить ждать, пока не выполнится что-либо во-2м...
а то что по сути это станет одним потоком, просто с разнесенным кодом/логикой на два тебя не смущает?
получается типа, "хочу нести воду в 2х ведрах, но так чтобы пол пути в одном, пол в другом. как на полпути воду в ведрах местами поменять? емкостей то больше нет".
← →
PacMan © (2012-05-01 20:52) [2]меня это не смущает, т.к. по заданию мне нужно копировать файл в 2-х потоках, и препод ничего не может мне подсказать по этому поводу.
Если у кого-то есть "знание" как сделать хотя бы это, буду благодарен.
Если кто-то может показать как сделать оптимальнее, также буду благодарен.
← →
sniknik © (2012-05-01 21:27) [3]> как 1 заставить ждать, пока не выполнится что-либо во-2м...
уже используется - CriticalSection.Enter; во втором заставит первый ждать, не скажу насчет правильности, логики его использования у тебя, но это вот оно.
и кстати у тебя куча всего чего нельзя в потоках... типа прямого обращения к vcl.
← →
PacMan © (2012-05-01 21:36) [4]ну синхронайз пока не трогаю... программа запускается и так.
при запуске я выбираю исходный файл, выбираю куда и под каким именем его скопировать...
по кнопке "что-то происходит" и в конце выводится "копирование успешно завершено".
открываю директорию назначения и вижу файлик с нужным именем... !НО! его размер 0 байт.
смотрел пошаговое выполнение, у меня запускается 1-й поток, проходит круг... ииииии..... "копирование завершено".
чтобы было понятнее что и как, могу на почту скинуть прогу.
← →
QAZ (2012-05-01 22:16) [5]
> sniknik © (01.05.12 20:44) [1]
там на самом деле гинеальная идея заложена :)
типа пока один поток пишет первый кусок,другой читает второй и тд
но она имеет смысл только при наличии 2х жестких дисков , если источник на одном а приемник на другом
← →
Сергей М. © (2012-05-01 22:18) [6]
> 1-й поток читает файл и пишет данные в буфер1, затем содержимое
> буфера 1 передает в буфер 2.
> 2-й поток пишет из буфера 2 в файл.
Это ты сам придумал или препод навязал тебе именно такую логику ?
← →
sniknik © (2012-05-01 22:50) [7]оптимальностью не пахнет, сама логика кривая, но... более... не знаю даже как сказать, но работоспособно это точно. -
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
{$R *.dfm}
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.
← →
sniknik © (2012-05-01 23:05) [8]кстати в цикле второго потока (или вообще) ошибка... он не завершается. исправь.
← →
Sha © (2012-05-02 02:08) [9]http://www.delphimaster.net/view/2-1202729073
← →
PacMan © (2012-05-02 10:24) [10]
> Это ты сам придумал или препод навязал тебе именно такую
> логику ?
на самом деле задание звучит так:
"Разработать компонент, реализующий копирование файлов, при помощи тройной буферизации. Программа должна использовать потоки. Первый поток считывает блок данных из указанного файла. Второй поток записывает этот блок данных в файл. Буферизацию реализовать из 2-х массивов, состоящих из 3-х элементов. Каждый элемент - это буфер. Первый массив отвечает за чтение из файла, а второй за запись. Первый поток получает имя файла, берет пустой элемент из первого массива, записывает в него блок данных и передает второму массиву этот блок данных. При этом буфер, в котором считали блок данных, обнуляется. Второй поток, видя, что во втором массиве есть полный буфер, записывает этот буфер в файл и обнуляет его. При этом, в первом массиве выделяется новый буфер. И так пока весь файл не будет скопирован"
!НО! я даже малейшего представления не имею как это реализовать. Вот и решил упростить "слегка".
если у кого-нибудь есть идеи как это сделать, или еще лучше исходники... буду просто счастлив))
надеюсь не сильно запутал.
← →
PacMan © (2012-05-02 10:30) [11]Есть коммерческое предложение, т.к. сам это все равно не напишу.
Если кто желает заморочиться по заданию, договоримся о цене.
Кто надумает, пишите в мыло yrungxay@mail.ru
← →
Sha © (2012-05-02 11:36) [12]> PacMan © (02.05.12 10:24) [10]
Формулировка ужасная. Это точно оригинальный текст "от препода"?
← →
PacMan © (2012-05-02 11:52) [13]ага)
это сфотографировано лично)
← →
Cobalt © (2012-05-02 11:54) [14]Имитация обработки - один поток откуда-то получает данные, складирует их в буфер.
Второй поток - мониторит поступлени данных, и записывает их в файл, например, в базу данных.
Вполне жизненная ситуация.
← →
Sha © (2012-05-02 12:57) [15]> PacMan © (02.05.12 10:24) [10]
> при помощи тройной буферизации.
обычно говорят буферизации чего, какой операции
> Программа должна использовать потоки
ну раз надо, значит, надо
> Буферизацию реализовать из 2-х массивов, состоящих из 3-х элементов.
всего 3 или 3 в каждом массиве? элементы - это что? обычно указатели
> Первый массив отвечает за чтение из файла, а второй за запись.
переменная ни за что никому не отвечает
> Первый поток получает имя файла,
> берет пустой элемент из первого массива,
> записывает в него блок данных..
значит, элемент - не указатель?
> и передает второму массиву этот блок данных.
передать переменной? она же памятник
копирование данных или указателей?
кто об этом и как узнает?
а если некуда передавать? табличка "занято" 3 раза?
> При этом буфер, в котором считали блок данных, обнуляется.
нафига? если в нем были данные - то это бред
может, указатель, хотя это тоже не оптимал
> Второй поток, видя, что во втором массиве есть полный буфер,
просто песня! КАК он это видит?
> записывает этот буфер в файл и обнуляет его.
опять же нафига?
опять же, если видит, но не может? типа еще е все доделал
> При этом, в первом массиве выделяется новый буфер.
вроде мы не удаляли ничего,
в таком случае лимит в 3 буфера будет превышен!
> Cobalt
Конечно, можно мониторить поступление телефонного звонка,
но, вероятно, намного проще брать трубку по звонку.
← →
Anatoly Podgoretsky © (2012-05-02 13:43) [16]> Sha (02.05.2012 12:57:15) [15]
Это тест на испытание копипастеров, не выживет так не выживет.
← →
PacMan © (2012-05-02 19:41) [17]
> Sha © (02.05.12 12:57) [15]
Это текст задания слово в слово.
Причем сама препод не может ни чего пояснить по нему.
Спрашиваю из элементов какого типа состоят массивы(массив байт или строки или еще что)? Отвечает, что как мне удобнее пусть так и будет, а про копирование вообще ни чего сказать не может.
как-то так.
← →
Сергей М. © (2012-05-02 20:44) [18]
> сама
Это многое что объясняет)
← →
Андреевич (2012-05-02 21:13) [19]
> там на самом деле гинеальная идея заложена :)
> типа пока один поток пишет первый кусок,другой читает второй
> и тд
> но она имеет смысл только при наличии 2х жестких дисков
> , если источник на одном а приемник на другом
или если источники типа \\server\share\file.txt
← →
Anatoly Podgoretsky © (2012-05-02 21:36) [20]> PacMan (02.05.2012 19:41:17) [17]
А что ты ей плохого сделал?
← →
Сергей М. © (2012-05-02 22:43) [21]
> Anatoly Podgoretsky © (02.05.12 21:36) [20]
А может просто не сделал, притом что должен был сделать ?
imho, сей вариант наиболее вероятен)
← →
Sha © (2012-05-03 01:20) [22]> PacMan ©
Почитай про кольцевой буфер клавиатуры,
прерывания 9 и 16, можно поверхностно.
Изобрази ей нечто похожее.
← →
Германн © (2012-05-03 01:54) [23]
> Sha © (03.05.12 01:20) [22]
Ностальгия?
← →
han_malign (2012-05-03 09:07) [24]
> Почитай про кольцевой буфер клавиатуры
- низзя, сказано "тройная буферизация", значит так и надо делать - нормальный 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-половинки")...
← →
PacMan © (2012-05-03 09:37) [25]
> Anatoly Podgoretsky © (02.05.12 21:36) [20]
хотел другое задание взять... сказала: "Нет, оно не интересное, я тебе вот это дам..."
и вот что заимел(
← →
Медвежонок Пятачок © (2012-05-03 09:44) [26]она на тебя просто запала. запишись на дополнительное индивидуальное занятие. вечернее.
← →
sniknik © (2012-05-03 09:45) [27]> и вот что заимел(
заслуженная двойка по предмету... даже видя перед собой решение, не понимаешь, что это такое.
← →
PacMan © (2012-05-03 11:13) [28]))) Решение вижу, понимаю алгоритм... не понимаю как реализовать.
В моем примере 1-й поток начинает работать, но проходит только 1 круг, из-за того что 2-й поток не хочет работать.
← →
sniknik © (2012-05-03 12:01) [29]> ))) Решение вижу, понимаю алгоритм... не понимаю как реализовать.
угадал все буквы, не смог назвать слово... это нельзя назвать "понимаю".
> В моем примере 1-й поток начинает работать, но проходит только 1 круг, из-за того что 2-й поток не хочет работать.
твой пример, это набор функций почти каждая из которых "не в том месте и не в то время".
вот что ты сделал по поводу
> и кстати у тебя куча всего чего нельзя в потоках... типа прямого обращения к vcl.
?
а ведь это может приводить к блокировкам, "локированию" потока. ... не думаю что ты "нарвался" именно на это (более вероятно кривая логика), но рассматривать код в котором такие очевидные глюки вряд ли кто будет (я точно нет).
← →
Медвежонок Пятачок © (2012-05-03 12:21) [30]стартуем два потока.
первый распределяет буфер и читает файл.
заполнив буфер, первый поток посылает средмессадж второму и сообщает адрес и длину буфера.
второй поток вынимает данные буфера в файл, затем грохает буфер и усыпает.
первый поток, закончив с одним буфером, распределяет второй и снова оповещает второй поток о готовой порции данных.
второй поток просыпается и повторяет операцию.
после того, как файл прочитан целиком, первый поток оповещает второй, что можно закругляться и идти пить пиво.
← →
Sha © (2012-05-03 12:23) [31]> han_malign (03.05.12 09:07) [24]
В данном случае пул и есть кольцевой буфер,
т.к. мы имеем дело с последовательными операциями чтения и записи.
Если уж делать по теории,
то поток чтения занимается только чтением и ничего не знает о потоке записи,
а поток записи занимается только записью и ничего не знает о потоке чтения.
← →
sniknik © (2012-05-03 12:48) [32]> Если уж делать по теории,
> то поток чтения занимается только чтением и ничего не знает о потоке записи,
> а поток записи занимается только записью и ничего не знает о потоке чтения.
посмотри код в [7], так и сделано.
условие завершения второго потока только забыл, 1 строка, после решил оставить это автору топика.
← →
Sha © (2012-05-03 14:01) [33]> sniknik © (03.05.12 12:48) [32]
сразу бросилось в глаза,
что код [7] легко исчерпает память при быстром чтении и медленной записи
← →
han_malign (2012-05-03 14:10) [34]
> что код [7] легко исчерпает память при быстром чтении и медленной записи
- и будет утечка, если PostThreadMessage произойдет раньше оппозитного GetMessage...
← →
Медвежонок Пятачок © (2012-05-03 14:19) [35]что код [7] легко исчерпает память при быстром чтении и медленной записи
какая разница, если даже это реально произойдет?
это же абстрактное дурацкое учебное задание ради самого задания.
← →
Anatoly Podgoretsky © (2012-05-03 14:23) [36]А может от него надо избавиться, тогда задание предстает совсем в другом свете.
← →
sniknik © (2012-05-03 14:26) [37]> что код [7] легко исчерпает память при быстром чтении и медленной записи
естественно. но цель примера не в "копипасте", как понял задача учебная.
а сделать счетчик/ограничитель на например 5 буферов, не проблема...
> - и будет утечка, если PostThreadMessage произойдет раньше оппозитного GetMessage...
с чего это оно произойдет раньше? первый поток только создается когда у второго уже ThreadID есть, т.е. уже "крутится".
← →
sniknik © (2012-05-03 14:28) [38]> это же абстрактное дурацкое учебное задание ради самого задания.
+1
на понимание. которого автор якобы достиг ([28]) но почему то все одно "не получается".
← →
sniknik © (2012-05-03 14:35) [39]>> - и будет утечка, если PostThreadMessage произойдет раньше оппозитного GetMessage...
> с чего это оно произойдет раньше? первый поток только создается когда у второго уже ThreadID есть, т.е. уже "крутится".
или имеется ввиду промежуток между стартом, и началом ожидания, там где пара команд открытия файла (если задержка), ну и что там дельфя своего добавляет?
в этом случае событие будет ждать в очереди, пока его явно из нее не выкинут. т.е. однозначно дождется цикла.
← →
Вариант (2012-05-03 15:03) [40]
> sniknik © (03.05.12 14:35) [39]
> с чего это оно произойдет раньше? первый поток только создается
> когда у второго уже ThreadID есть, т.е. уже "крутится"
Может начать крутиться и быть остановленным системой, не дойдя до первого вызова GetMessage. В этом случае еще не будет очереди сообщений потока, тогда PostThreadNessage вернет fails. Вероятность этого не нулевая, хотя и повторяемость события не очень большая (если не использовать например отладчик для эмуляции), сам натыкался на такое, хотя первой командой в Execute потока был PeekMessage, просто поток был остановлен планировщиком. Поэтому было бы неплохо после PeekMessage сигналить в запускающий поток, что мой поток готов принимать сообщения и только потом уже разрешать слать ему сообщения..... ну или вариант по MSDN сценарию...
← →
Вариант (2012-05-03 15:11) [41]А автору поста посоветовал бы Рихтера с Кларком почитать, "Программирование серверных приложений для Windows 2000". Там готовый код на си есть копирования файлов с использованием потоков. Правда придется разбираться и читать, и пробовать и на дельфи перетаскивать. Но зато интересно.
← →
PacMan © (2012-05-04 11:12) [42]Ладно, всем спасибо, буду тыкаться. Посмотрим что выйдет.
← →
Медвежонок Пятачок © (2012-05-04 11:13) [43]смотря чем тыкаться.
если головой, то она первой и выйдет с другой стороны.
Страницы: 1 2 вся ветка
Форум: "Начинающим";
Текущий архив: 2013.03.22;
Скачать: [xml.tar.bz2];
Память: 0.6 MB
Время: 0.11 c