Многопоточное программирование на C++
Реферат, 24 Января 2012, автор: пользователь скрыл имя
Краткое описание
Для начала поговорим о подводных камнях C++ применительно к MT программированию. На первый взгляд может показаться, что написанные на C++ потоки, не изменяющие одни и те же данные одновременно, могут работать абсолютно независимо и параллельно. К сожалению, это не совсем так: основной проблемой является стандартный распределитель памяти, т.е. операторы new/delete.
Прикрепленные файлы: 1 файл
СПО.docx
— 67.57 Кб (Скачать документ)Как следствие, doFindFiles() создает сохраняемые имена директорий и файлов с помощью ownmp:
void MainTask::doFindFiles(mem_
msg)
{
int pref=msg.dir->name->size();
vector<sh_text> dirs;
for (dirs.push_back(msg.dir->name)
sh_dir shd=new_dir(mp, dirs.back());
dirs.pop_back();
for (dir::entry dent(mp); shd->find_next(dent); ) {
if (dent.name=="." || dent.name=="..") continue;
sh_text fname=shd->full_name(dent);
if (dent.isdir) dirs.push_back(fname);
assert(fname->size()>=pref+1);
int beg= (fname->begin()[pref]==
sh_text name(nt(msg.dir->ownmp, fname->begin()+beg, fname->end()));
if (dent.isdir) msg.dir->dirs.insert(name, 0);
else msg.dir->files.insert(name, dent.size);
}
}
}
А doCompare() и doCopyFile() запоминают найденные имена (факт. ключи hash_vec<sh_text, ...>-ра) по ссылке, а не значению:
const sh_text& dir=dc1->dirs.key(i);
// ...
const
sh_text& fil=dc1->files.key(i);
// ...
const sh_text& name=oldDir.files.key(msg.
Т.к. сохранение по значению приведет к копированию объекта sh_ptr<text> и, как следствие, одновременному использованию ownmp несколькими рабочими потоками. Тем самым в программе появится ошибка синхронизации, т.е. та самая "великая и ужасная" race condition, непонятно когда и как проявляющаяся.
И если желаете, то в качестве неприятного, но весьма поучительного упражнения можно и в самом деле заменить ссылки на значения и попытаться ее отследить...
Изменившийся файл может находиться достаточно глубоко в структуре сравниваемых поддиректорий, поэтому его копирование в директорию diff_dir/mod/... может потребовать предварительного создания недостающих промежуточных поддиректорий. Задача осложняется еще и тем, что подлежащие созданию поддиректории могут быть созданы параллельно работающими потоками в промежутке между временем обнаружения их отсутствия и попыткой их создать. Для эффективного решения данной проблемы приложение пробует сразу же создать несуществующий файл и только в случае ошибки выполняется попытка создания промежуточных директорий и повторная попытка создания файла:
file fout(mp);
if (!fout.open(msg.toName, file::wro, file::crt|file::trnc)) {
make_dirs(mp, get_path(mp, msg.toName));
fout.ex_open(msg.toName, file::wro, file::crt|file::trnc);
}
Обратите внимание,
что для повторного создания файла
вместо функции open() используется вызов ex_open(),
автоматически возбуждающей исключения.
2. Динамически загружаемые библиотеки
Понятие динамически подключаемых библиотек.
Библиотека DLL представляет собой коллекцию подпрограмм, которые могут быть вызваны на выполнение приложениями или подпрограммами из других библиотек. Подобно модулям, библиотеки DLL содержат разделяемый (sharable) код или ресурсы. Однако, в отличие от модулей, библиотеки содержат отдельно откомпилированный исполняемый код, который подключается к приложению динамически на этапе его выполнения, а не компиляции.
Для того чтобы библиотеки можно было отличить от самостоятельно выполняемых приложений, они имеют расширение .dll. Программы, написанные на Object Pascal, могут использовать библиотеки, написанные на других языках. С другой стороны, приложения Windows могут использовать библиотеки, написанные на Object Pascal.
Библиотеки могут содержать два вида подпрограмм: экспортируемые и внутренние. Экспортируемые подпрограммы могут вызываться процессом, подключающим библиотеку, а внутренние могут быть вызваны только внутри библиотеки.
Библиотека загружается в адресное пространство процесса, который ее вызвал. Подпрограммы библиотеки также используют стековое пространство процесса и его динамическую память.
Преимущества использования динамических библиотек (по сравнению со статическим подключением подпрограмм на этапе сборки приложения) следующие:
1 Процессы, которые
загрузили библиотеку по
2 Модификация
подпрограмм библиотек не
3 Библиотеки
обеспечивают также
4 Программы,
написанные на разных языках
программирования, могут использовать
одну и ту же библиотечную
функцию, если они следуют
Интерфейс прикладных программ (Microsoft Win32 application programming interface – API) реализован как набор DLL–библиотек.
Недостатком использования библиотек является то обстоятельство, что приложение не является самодостаточным и зависит от наличия отдельного файла(ов) библиотеки. В случае отсутствия статически загружаемой библиотеки система прерывает приложение и выводит сообщение об ошибке. В случае отсутствия динамически загружаемой библиотеки приложение не прерывается, но функции библиотеки являются, разумеется, недоступными для приложения.
Вызов подпрограмм из библиотек DLL
Прежде чем
программа сможет вызвать из библиотеки
подпрограмму, ее необходимо импортировать.
Сделать это можно двумя
Необходимо отметить, что Object Pascal не поддерживает импорт переменных из DLL.
Статическая загрузка подпрограмм
Простейший способ импорта подпрограммы из библиотеки состоит в объявлении ее с помощью директивы external, например
procedure DoSomething; external 'MyLib.dll';
function MessageBox(HWnd: Integer;
Text, Caption: PChar; Flags: Integer):
Integer; stdcall; external 'user32.dll' name 'MessageBoxA';
// MessageBoxA – 'настоящее' имя функции в библиотеке
Если включить эти объявления в программу, библиотеки MyLib.dll и user32.dll будет загружена в память при запуске программы на выполнение. В течение всего времени выполнения программы идентификатор DoSomething или MessageBox будет ссылаться на одну и ту же точку входа в той же библиотеке.
Объявления импортируемых
подпрограмм можно поместить
непосредственно в программу
или модуль, которые их используют.
Вместе с тем использование
Пример использования статической библиотеки (полный текст см. в проекте UseDll):
Unit Unit1;
interface
uses Windows, …;
type
TForm1 = class(TForm)
…
end;
{Импортируемыефункции}
Function Max(a,b:integer):integer; external 'MyDll.dll';
Function Min(a,b:integer):integer; external 'MyDll.dll';
…
var
Form1: TForm1;
implementation
…
end.
Динамическая загрузка библиотек.
Приложение может самостоятельно загружать и выгружать библиотеки и получать доступ к их подпрограммам с использованием функции GetProcAddress. Для целей загрузки и выгрузки библиотек предназначены подпрограммы LoadLibrary и FreeLibrary. Пример их использования (полный пример см. в проекте UseDll_2):
Unit Unit1;
interface
uses Windows, …;
const DllName = 'MyDll.dll'; //можно указать 'MyDll'
type
TForm1 = class(TForm)
…
Procedure FormCreate ( Sender : TObject);
Procedure FormClose ( Sender: TObject; var Action: TCloseAction);
public
MyHandle : integer; {для получения результата выполнения функции
LoadLibrary}
end;
{Тип TFunction необходим для того, чтобы воспользоваться функцией
GetProcAddress для получения адреса необходимой функции из библиотеки}
TFunction = Function (a,b:integer) : integer;
var
Form1: TForm1;
FMax,FMin : TFunction;
implementation
{$R *.DFM}
Procedure TForm1.FormCreate ( Sender : TObject );
Begin
…
MyHandle := LoadLibrary ( DllName );
if MyHandle = 0 then
begin
ShowMessage('Ошибка при загрузке библиотеки ' + DllName);
Application.Terminate;
end;
@FMax := GetProcAddress(MyHandle,'Max')
@FMin := GetProcAddress(MyHandle,'Min')
if (@FMax=nil) or (@FMin=nil) then
begin
ShowMessage('Не найдены
необходимые функции в
FreeLibrary(MyHandle);
Application.Terminate;
end;
End;
…
{теперь подпрограммы можно вызывать используя имена FMax и FMin}
…
Procedure TForm1.FormClose( Sender : TObject; var Action: TCloseAction);
Begin
{Выгружаем библиотеку из памяти}
FreeLibrary(Handle);
End;
…
END.
В этом способе использования библиотеки ее загрузка в память не выполняется до вызова функции LoadLibrary, что позволяет экономить память. Кроме того, в этом случае можно выполнять программу даже в том случае, если какие-либо библиотеки не найдены (если, разумеется, в этом есть какой-либо смысл). Чтобы получить более подпробную информацию об ошибке, надо использовать функцию GetLastError.
Поиск загружаемой библиотеки, если ее имя не содержит пути, выполняется по следующим маршрутам (см. Help для функции LoadLibrary):
1 В каталоге, из которого было запущено приложение.
2 В текущем каталоге.
3 В каталоге System (Windows 95) или в каталоге System32 (Windows NT). Для получения пути к этому каталогу можно использовать функцию GetSystemDirectory.
4 Для Windows NT: в каталоге SYSTEM (16-битная версия).
5 В каталоге Windows. Для получения пути к этому каталогу можно использовать функцию GetWindowsDirectory.
6 В каталогах, перечисленных в переменной окружения PATH.
При попытке повторной загрузки библиотеки ошибки не происходит и библиотека не загружается.
В любой момент
временибиблиотеку можно
3. Работа с графикой
Создание многодокументного приложения
До сих пор мы работали с программой, поддерживающей одновременную работу лишь с одним документом-изображением (SDI-интерфейс). Во многих случаях такая организация интерфейса вполне приемлема. Рассмотрим далее, как можно создать приложение с многодокументным интерфейсом (MDI-интерфейсом). Особых усилий от нас не потребуется. Всю основную работу сделает генератор приложений AppWizard.