Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции КПиЯП.docx
Скачиваний:
50
Добавлен:
20.09.2019
Размер:
3.8 Mб
Скачать

Асинхронные делегаты

Делегат можно вызвать на выполнение либо синхронно, как во всех приведенных ранее примерах, либо асинхронно с помощью методов BeginInvoke и EndInvoke.

При вызове делегата с помощью метода BeginInvoke среда выполнения создает для исполнения метода отдельный поток и возвращает управление оператору, следующему за вызовом. При этом в исходном потоке можно продолжать вычисления.

Если при вызове BeginInvoke был указан метод обратного вызова, этот метод вызывается после завершения потока. Метод обратного вызова также задается с помощью делегата, при этом используется стандартный делегат AsyncCallback. В методе обратного вызова для получения возвращаемого значения и выходных параметров применяется метод EndInvoke.

Если метод обратного вызова не был указан в параметрах метода BeginInvoke, метод EndInvoke можно использовать в потоке, инициировавшем запрос.

Лекция 5.1. Методы конструирования сложных программных систем

Под программированием на нескольких языках мы будем понимать разработку одной программы с использованием нескольких языков и взаимодействие программ, написанных на разных языках

В программах на Visual C++ и Delphi можно делать ассемблерные вставки. Это называется inline-ассемблером. Обычно inline-ассемблер используется в двух случаях:

  • Если нужно оптимизировать критичные по скорости и небольшие по объему участки программы. грамотно написанный ассемблерный код всегда быстрее кода, который генерируется компилятором c++ или delphi.

  • Нужен прямой доступ к памяти и портам. чаще всего используется в драйверах, так как из третьего кольца защиты с портами не очень-то поработаешь.

Поскольку информации об inline-ассемблерах в интернете довольно мало, мы остановимся на этом подробнее.

Inline-ассемблер в Visual C++

Ассемблерные вставки в исходниках Visual C++ оформляются с помощью блока __asm и выглядят примерно так:

// обычный код на C++

printf("Hello!\n");

// а тут ассемблерная вставка

__asm {

mov eax, 2

mov edx, 7

add eax, edx

}

// а потом снова обычный код на C++

printf("Bye!");

Или, что то же самое, так:

// ассемблерная вставка

__asm mov eax, 2

__asm mov edx, 7

__asm add eax, edx

А можно вообще в одну строчку:

// ассемблерная вставка

__asm mov eax, 2 __asm mov edx, 7 __asm add eax, edx

В inline-ассемблере Visual C++ можно использовать все инструкции вплоть до Pentium 4 и AMD Athlon. Поддерживается также MMX. Поддержки Itanium и x64 пока нет :(.

По синтаксису inline-ассемблер Visual C++ частично совпадает с MASM. Например, как и в MASM можно строить выражения с операндами и использовать offset с глобальными переменными (смотри листинг 1). Как и в MASM, можно использовать глобальные метки и принудительно определять короткие переходы (смотри листинг 2). Однако определить локальную метку с помощью @@ в inline-ассемблере Visual C++ не получится – замена lab: на @@lab: в предыдущем примере вызовет ошибку компиляции. Есть и другие отличия от MASM. Например, в inline-ассемблере Visual C++ нет никаких средств для объявления переменных, поэтому про привычные DB, DW, DD, DQ, DT, DF, DUP и THIS можно забыть. Зато можно использовать переменные, объявленные в программе на C++ (смотри листинг 3).

В inline-ассемблере также нельзя объявлять структуры — директивы STRUC, RECORD, WIDTH и MASK недопустимы. Вместо этого можно использовать структуры, объявленные в программе на C++ (смотри листинг 4). Кроме того, в inline-ассемблере можно использовать комментарии и HEX-числа в стиле C++.

В принципе, все отличия подробно описаны в MSDN 2006 (идет в комплекте с Visual Studio 2006).

Обычно в Visual C++ inline-ассемблер используется для написания функций. Как правило, функции, написанные на inline-ассемблере, объявляют с использованием директивы _stdcall. В этом случае параметры передаются в функцию на стеке в обратном порядке, а результат работы возвращается в eax.

Для примера рассмотрим функцию, которая находит длину ASCIIZ-строки:

int _stdcall get_str_length(char *inputstr)

{

// чаще всего функция целиком состоит из одной

// большой ассемблерной вставки

__asm{

; можно свободно обращаться к переменным,

; переданным в функцию

mov edi, inputstr

mov esi, edi

mov ecx, -1

xor al, al

cld

repne scasb

sub edi, esi

; результат работы функции следует

; возвращать в eax

mov eax, edi

dec eax

}

}

Использовать эту функцию можно так:

int main()

{

char str_1[]="Hello, world!";

int i;

i = get_str_length(str_1);

printf("String: %s\nLength: %d", str_1, i);

}

При написании кода на inline-ассемблере Visual C++ следует помнить некоторые моменты. Во-первых, значения регистров не передаются между ассемблерными вставками. Например, если в ассемблерной вставке ты установил eax в 1, то в следующей ассемблерной вставке eax не обязательно будет равно 1 (смотри листинг 5).

Во-вторых, нужно быть осторожным с регистрами и стеком. В середине ассемблерных вставок нельзя менять регистры ds, ss, sp, bp и флаги. Если эти регистры все-таки меняются, перед выходом из ассемблерной вставки их нужно обязательно восстановить. Что касается стека, то тут нужно соблюдать правило, которое гласит: если в ассемблерной вставке нечто ложится на стек, то в той же ассемблерной вставке это «нечто» должно со стека сниматься.

Рассмотрим, например, такой код:

#include <stdio.h>

// наша функция на ассемблере

void _stdcall Test()

{

__asm{

; кладем на стек eax

push eax

; перед тем как ассемблерная вставка кончится, нужно

; снять eax со стека (например, с помощью pop),

; но мы забыли это сделать ;)

}

}

int main()

{

// вызываем функцию... и любуемся сообщением об ошибке :)

Test();

}

И, наконец, если ты пишешь не драйвер, а обычное Win32-приложение, в ассемблерной вставке не должно быть привилегированных инструкций. Вот, пожалуй, и все про inline-ассемблер Visual C++. Тем, кто хочет узнать больше, советуем почитать MSDN 2006 – там есть вся необходимая информация.