Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Методичка по С#

.pdf
Скачиваний:
276
Добавлен:
13.02.2015
Размер:
3.13 Mб
Скачать

171

// объект синхронизации.

sender0 = new Sender(ref queueX, new CallBackFromStartClass(StopMain), 0); sender1 = new Sender(ref queueX, new CallBackFromStartClass(StopMain), 1); receiver = new Receiver(ref queueX, new CallBackFromStartClass(StopMain), 2);

//Стартовые функции потоков должны соответствовать сигнатуре

//класса делегата ThreadStart. Поэтому они не имеют параметров. t0 = new ThreadStart(sender0.startSender);

t1 = new ThreadStart(sender1.startSender);

t2 = new ThreadStart(receiver.startReceiver);

//Созданы вторичные потоки.

th0 = new Thread(t0); th1 = new Thread(t1); th2 = new Thread(t2);

th0.Start();

th1.Start();

th2.Start();

th0.Join();

th1.Join();

th2.Join();

Console.WriteLine

(“Main(): “ + report[0] + “...” + report[1] + “...” + report[2] + «... Bye.»);

}

//Функция-член класса StartClass выполняется во ВТОРИЧНОМ потоке! public void StopMain(string param)

{

Console.WriteLine(«StopMain: « + param);

//Остановка рабочих потоков. Её выполняет функция-член

//класса StartClass. Этой функции в силу своего определения

//известно ВСЁ о вторичных потоках. Но выполняется она

//в ЧУЖИХ (вторичных) потоках.

if (param.Equals(“Sender0”))

{

report[0] = “Sender0 all did.”; th0.Abort();

}

if (param.Equals(“Sender1”))

{

report[1] = “Sender1 all did.”; th1.Abort();

}

if (param.Equals(“Receiver”))

{

report[2] = “Receiver all did.”; th2.Abort();

}

if (param.Equals(“0”))

{

th1.Abort();

th2.Abort();

th0.Abort();

}

if (param.Equals(“1”))

{

th0.Abort();

th2.Abort();

th1.Abort();

}

if (param.Equals(“2”))

{

th0.Abort();

th1.Abort();

171

172

th2.Abort();

}

//Этот оператор не выполняется! Поток, в котором выполняется

//метод-член класса StartClass StopMain() остановлен.

Console.WriteLine(“StopMain(): bye.”);

}

}

}//----------------------------------------------------------------------------------

2. Специальные возможности мониторов

Класс Monitor управляет доступом к коду с использованием объекта синхронизации. Объект синхронизации предоставляет возможности для ограничения доступа к блоку кода, в общем случае обозначаемого как критическая секция.

Поток выполняет операторы. Выполнение оператора, обеспечивающего захват с помощью монитора объекта синхронизации, закрывает критическую секцию кода.

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

Таким образом, монитор гарантирует, что никакой другой поток не сможет

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

помощью монитора

и

данного объекта синхронизации критическую секцию кода,

пока она

не будет

освобождена, если только потоки не выполняют данную секцию

кода

с

использованием другого объекта синхронизации.

Следующая таблица описывает действия, которые могут быть предприняты потоками при взаимодействии с монитором:

Действие

 

Описание

 

 

 

 

 

 

Enter, TryEnter

 

Закрытие секции с помощью объекта синхронизации. Это

 

 

действие также обозначает начало критической секции. Никакие

 

 

другие потоки не могут войти в заблокированную критическую

 

 

секцию, если только они не используют другой объект

 

 

синхронизации.

 

 

 

 

 

 

 

 

Exit

 

Освобождает блокировку критической секции кода. Также

 

 

обозначает конец критической секции, связанной с данным

 

 

объектом синхронизации.

 

 

 

 

Wait

 

Поток переходит в состояние ожидания, предоставляя тем самым

 

 

другим потокам возможность выполнения других критических

 

 

секций кода, связанных с данным объектом синхронизации. В

 

 

состоянии ожидания поток остаётся до тех пор, пока на выходе

 

 

из другой секции, связанной с данным объектом синхронизации,

 

 

другой поток не выполнит на мониторе действия Pulse

 

 

(PulseAll), которые означают изменение состояния объекта

 

 

синхронизации и обеспечивают выход потока из состояния

 

 

ожидания на входе в критическую секцию.

 

 

 

 

 

 

 

 

 

 

Pulse

(signal),

Посылает

сигнал

ожидающим

потокам.

Сигнал

служит

PulseAll

 

уведомлением ожидающему потоку, что состояние объекта

 

 

синхронизации изменилось, и что владелец объекта готов его

 

 

освободить.

Находящийся

в

состоянии

ожидания

поток

 

 

фактически находится в очереди для получения доступа к

 

 

объекту синхронизации.

 

 

 

 

Enter и Exit иетоды используются для обозначения начала или конца критической секции. Если критическая секция представляет собой “непрерывное” множество инструкций, закрытие кода посредством метода Enter гарантирует, что только один единичный поток сможет выполнять код, закрытый объектом синхронизации.

Рекомендуется размещать эти инструкции в try block и помещать Exit instruction в finally блоке.

Все эти возможности обычно используются для синхронизации доступа к статическим и нестатическим методам класса. Нестатический метод блокируется посредством объекта синхронизации. Статический объект блокируется с использованием типа (класса, членом которого данный метод является).

172

173

///<summary>

///Синхронизация потоков с использованием класса монитора.

///Синхронизация потоков с использованием класса монитора.

///Монитор защищает очередь от параллельного вторжения со стороны

///взаимодействующих потоков из разных фрагментов кода.

///Однако монитор не может защитить потоки от взаимной блокировки.

///Поток просыпается, делает свою работу, будит конкурента, засыпает сам.

///К тому моменту, как поток будит конкурента, конкурент должен спать.

///Активизация незаснувшего потока не имеет никаких последствий.

///Если работающий поток разбудит не успевший заснуть поток - возникает

///тупиковая ситуация. Оба потока оказываются погруженными в сон.

///В этом случае имеет смысл использовать перегруженный вариант метода

///Wait - с указанием временного интервала.

///</summary>

using System;

using System.Threading; using System.Collections;

namespace MonitorCS1

{

class MonitorApplication

{

const int MAX_LOOP_TIME = 100; Queue xQueue;

public MonitorApplication()

{

xQueue = new Queue();

}

public void FirstThread()

{

int counter = 0; while(counter < MAX_LOOP_TIME)

{

Console.WriteLine(“Thread_1___”); counter++;

Console.WriteLine(“Thread_1...{0}”, counter);

try

{

//Push element. xQueue.Enqueue(counter);

foreach(int ctr in xQueue)

{

Console.WriteLine(“:::Thread_1:::{0}”, ctr);

}

}

catch (Exception ex)

{

Console.WriteLine(ex.ToString());

}

//Release the waiting thread. Применяется к конкурирующему потоку. lock(xQueue){Monitor.Pulse(xQueue);}

Console.WriteLine(“>1 Wait<”);

//Wait, if the queue is busy. Применяется к текущему потоку. // Собственное погружение в состояние ожидания. lock(xQueue){Monitor.Wait(xQueue,1000);}

Console.WriteLine(“!1 Work!”);

}

Console.WriteLine(“*****1 Finish*****”); lock(xQueue) {Monitor.Pulse(xQueue);}

173

174

}

public void SecondThread()

{

int counter = 0; while(counter < MAX_LOOP_TIME)

{

//Release the waiting thread. Применяется к конкурирующему потоку. lock(xQueue){Monitor.Pulse(xQueue);}

Console.WriteLine(«>2 Wait<»);

// Собственное погружение в состояние ожидания. lock(xQueue){Monitor.Wait(xQueue,1000);}

Console.WriteLine(“!2 Work!”);

Console.WriteLine(“Thread_2___”);

try

{

foreach(int ctr in xQueue)

{

Console.WriteLine(“:::Thread_2:::{0}”, ctr);

}

//Pop element.

counter = (int)xQueue.Dequeue();

}

catch (Exception ex)

{

counter = MAX_LOOP_TIME; Console.WriteLine(ex.ToString());

}

Console.WriteLine(“Thread_2...{0}”,counter);

}

Console.WriteLine(“*****2 Finish*****”); lock(xQueue) {Monitor.Pulse(xQueue);}

}

static void Main(string[] args)

{

//Create the MonitorApplication object. MonitorApplication test = new MonitorApplication();

Thread tFirst = new Thread(new ThreadStart(test.FirstThread));

//Вторичные потоки созданы!

Thread tSecond = new Thread(new ThreadStart(test.SecondThread)); //Start threads.

tFirst.Start();

tSecond.Start();

// Ждать завершения выполнения вторичных потоков. tFirst.Join();

tSecond.Join();

}

}

}

Рекомендации по недопущению блокировок потоков

Соблюдать определённый порядок при выделении ресурсов.

При освобождении выделенных ресурсов придерживаться обратного (reverse) порядка.

Минимизировать время неопределённого ожидания выделяемого ресурса.

Не захватывать ресурсы без необходимости и при первой возможности освобождать захваченные ресурсы.

Захватывать ресурс только в случае крайней необходимости.

В случае если ресурс не удаётся захватить, повторную попытку его захвата производить только после освобождения ранее захваченных ресурсов.

174

175

Максимально упрощать структуру задачи, решение которой требует захвата ресурсов. Чем проще задача – тем на меньший период времени захватывается ресурс.

175

176

Форма

Класс Form

Форма является представлением окна, которое появляется в Windows приложении. Это класс, который можно использовать как основу для создания различных вариантов окошек: стандартных, инструментальных, всплывающих, borderless, диалоговых, and floating windows.

Создаётся “окно”, а класс называется формой – поскольку в окошке можно разместить элементы управления, обеспечивающие интерактивное взаимодействие приложения и пользователя (заполните форму, please).

Известна особая категория форм – формы (MDI) – формы с многодокументным интерфейсом (the multiple document interface).

Эти формы могут содержать другие формы, которые в этом случае называются MDI child forms. MDI форма создаётся после установки в true свойства

IsMdiContainer.

Форма это класс, включающий свойства, методы и события.

Используя доступные в классе Формы свойства, можно определять внешний вид, размер, цвет, и особенности управления создаваемого окна или диалога.

Свойство Text позволяет специфицировать надпись of the window in the title bar.

Свойства Size и DesktopLocation позволяют определять размеры и положение окна в момент его появления на экране монитора.

Свойство ForeColor позволяет изменить предопределённый foreground color всех элементов управления, placed on the form.

Свойства FormBorderStyle, MinimizeBox, and MaximizeBox позволяют to control whether the form can be minimized, maximized, or resized во время выполнения приложения.

Методы класса обеспечивают управление формой.

Например, метод ShowDialog обеспечивает представление формы как модального dialog box.

Метод Show показывает форму как немодальный dialog box.

Метод SetDesktopLocation используется для позиционирования формы на поверхности desktop.

Форма предназначена для реализации интерфейса пользователя приложения. Содержит большой набор свойств, методов, событий для реализации различных вариантов пользовательского интерфейса. Является окном и наследует классу

Control.

Это означает, что объект-представитель класса Form поддерживает механизмы управления, реализованные на основе обмена сообщениями Windows.

Структура сообщений и стандартные механизмы управления здесь не рассматриваются. Достаточно знать, что класс формы содержит объявление множества событий, для которых на основе стандартного интерфейса (сигнатуры) могут быть реализованы и легко подключены функции обработки событий.

Форма может использоваться как the starting class в приложении. При этом класс формы должен содержать точку входа – статический метод Main. В теле этого метода обычно размещается код, обеспечивающий создание и формирование внешнего вида формы.

Обычно заготовка формы “пишется” мастером. Пример кода простой заготовки окна прилагается. Форма настолько, что после некоторой медитации может быть воспроизведена вручную.

using System;

using System.Drawing; using System.Collections;

using System.ComponentModel; using System.Windows.Forms; using System.Data;

namespace FormByWizard

{

///<summary>

///Summary description for Form1.

///</summary>

176

177

public class Form1 : System.Windows.Forms.Form

{

///<summary>

///Required designer variable.

///</summary>

private System.ComponentModel.Container components = null;

public Form1()

{

//

//Required for Windows Form Designer support

InitializeComponent();

//TODO: Add any constructor code after InitializeComponent call

}

///<summary>

///Clean up any resources being used.

///</summary>

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (components != null)

{

components.Dispose();

}

}

base.Dispose( disposing );

}

#region Windows Form Designer generated code

///<summary>

///Required method for Designer support - do not modify

///the contents of this method with the code editor.

///</summary>

private void InitializeComponent()

{

this.components = new System.ComponentModel.Container(); this.Size = new System.Drawing.Size(300,300);

this.Text = “Form1”;

}

#endregion

///<summary>

///The main entry Point for the application.

///</summary>

[STAThread] static void Main()

{

Application.Run(new Form1());

}

}

}

Главная проблема – метод InitializeComponent(). Это зона ответственности мастера приложений и не рекомендуется в этом методе делать что-либо самостоятельно. Во избежание потери всего того, что там может быть построено, поскольку мастер приложения может изменить содержимое тела метода в соответствии с изменениями внешнего вида приложения.

В этом классе куча всяких методов. Интересно посмотреть на внушительный список обработчиков событий.

Важна строчка в теле функции Main

Application.Run(new Form1());

177

178

Впринципе эта строчка и отвечает за создание, “запуск” в потоке приложения

ивозможное появление на экране дисплея формы.

Форма: управление и события жизненного цикла

Управление жизненным циклом и отображением форм осуществляется следующими методами:

Form.Show(),

Form.ShowDialog(),

Form.Activate(),

Form.Hide(),

Form.Close().

Имеет смысл рассмотреть события, связанные с созданием, функционированием и уничтожением формы. К их числу относятся:

Load. Генерируется ОДИН РАЗ, непосредственно после первого вызова метода Form.Show() или Form.ShowDialog(). Это событие можно использовать для первоначальной инициализации переменных и для подготовки формы к работе. Сколько в приложении форм, столько раз будет генерироваться это событие. Назначение максимальных и минимальных размеров формы – для этого более подходящего места, нежели обработчик события OnLoad не найти!

Activated. Многократно генерируется в течение жизни формы. Когда Windows активизирует форму. Связано с получением и потерей фокуса. Все необходимые мероприятия выполняются здесь. Методы Form.Show(), Form.ShowDialog(), Form.Activate() (передача фокуса, реализованная программно!) способствуют этому. Передача фокуса элементу управления (кнопка – это тоже окно) сопровождается автоматическим изменением цвета элемента управления.

VisibleChanged. Генерируется всякий раз при изменении свойства Visible формы. Когда она становится видимой или невидимой. Событию способствуют методы Form.Show(), Form.ShowDialog(), Form.Hide(), Form.Close().

Deactivated. Возникает при потере фокуса формой в результате взаимодействия с пользовательским интерфейсом либо в результате вызова методов Form.Hide() или Form.Cloze() – но только для активной формы. Если закрывать неактивную форму, событие не произойдёт! Сказано, что Activated и Deactivated возбуждаются только при перемещении фокуса в пределах приложения. При переключении с одного приложения на другое эти события не генерируются.

Closing. Непосредственно перед закрытием формы. В этот момент процесс закрытия формы может быть приостановлен и вообще отменён, чему способствует размещаемый в теле обработчика события следующий программный код: e.Cancel = true; // e - событие типа CancelEventArgs.

Closed. Уже после закрытия формы. Назад пути нет. В обработчике этого события размещается любой код для “очистки” после закрытия формы.

Форма: контейнер как элемент управления

Container controls – место для группировки элементов пользовательского интерфейса. И это не простая группировка! Контейнер может управлять доступом к размещаемым не его поверхности элементам управления. Это обеспечивается за счёт свойства Enable, которое будучи установленным в false, закрывает доступ к размещаемым в контейнере компонентам. “Поверхность” контейнера у некоторых его разновидностей не ограничивается непосредственно видимой областью. Могут быть полосы прокрутки.

Каждый контейнер поддерживает набор элементов, который состоит из всех вложенных в него элементов управления. У контейнера имеются свойства Count, которое возвращает количество элементов управления, свойство [ИНДЕКСАТОР],

методы Add и Remove.

Кчислу контейнеров относятся:

Form… Без комментариев!

Panel. Элемент управления, содержащий другие элеменгты управления. You can use a Panel to для группировки множества элементов управления, как например,

группа of RadioButton controls. The Panel control is displayed по умолчанию БЕЗ всяческих рамок. Нет у ПАНЕЛИ и заголовка. Рамки можно заказать. You can

178

179

provide a standard or three-dimensional border using the BorderStyle property to distinguish the area of the panel from other areas on the form.

Panel является производным классом от the ScrollableControl class, а это значит, что можно заказать и полосы прокрутки, путём изменения значения соответствующего свойства. И всё, что располагается на панели за пределами видимости, при условии установки свойства AutoScroll в true, any controls located within the Panel (but outside of its visible region), can be scrolled to with the scroll bars provided.

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

TabControl. Содержит tab pages, которые представлены TabPage объектами, которые могут добавляться через свойство Controls. Каждая TabPage-страница подобна листку записной книжки: страница с закладкой. И в зависимости от того, подцеплены ли tab pages к объекту-представителю класса TabControl, будут генерироваться следующие события: Control.Click, Control.DoubleClick, Control.MouseDown, Control.MouseUp, Control.MouseHover, Control.MouseEnter, Control.MouseLeave and Control.MouseMove. If there is at least one TabPage in the collection, and the user interacts with the tab control’s header (where the TabPage names appear), the TabControl raises the appropriate event. However, if the user interaction is within the ClientRectangle of the tab page, the TabPage raises the appropriate event. От взаимодействия со страницей генерируются одни события, от взаимодействия с корешком – другие.

Разница между элементами управления и компонентами.

Элемент управления имеет видимое представление и непосредственно используется в процессе управления формой. Компоненты (таймер, провайдеры дополнительных свойств) видимого представления не имеют. И поэтому в управлении формой участвуют опосредованно.

Свойства элементов управления. Anchor и Dock

Средства, которые обеспечивают стыковку и фиксацию элементов управления в условиях изменяемых размеров контейнера.

Anchor позволяет автоматически выдерживать постоянное расстояние между границами элемента управления, для которого оно определено, и границами контейнера. И это обеспечивается за счёт автоматического изменения размеров “зацепленного” элемента управления вслед за изменениями размеров контейнера. Таким образом обеспечивается реакция элемента управления на изменения контейнера.

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

Extender providers. Провайдеры дополнительных свойств

An extender provider - компонент, который предоставляет свойства другим компонентам. Это делается с целью расширения множества свойств (элементов управления).

ToolTipProvider,

HelpProvider,

ErrorProvider.

Например, когда a ToolTip Component (Windows Forms) компонент добавляется к форме, все остальные компоненты, размещённые на этой форме, получают новое свойство, которое даже можно просматривать и устанавливать (редактировать) в окне Properties непосредственно в процессе разработки формы. И это новое свойство, называемое ToolTip, вызывается для каждого элемента управления данной формы.

Не надо обольщаться. Множество свойств элементов управления не изменилось. Дополнительное свойство предоставляется ВСЕМ элементам отдельным компонентом.

179

180

На этапе разработки, это свойство появляется в Properties window for the component that is being modified.

Однако при выполнении (at run time), это свойство не может быть доступно (accessed) непосредственно через данный конкретный элемент управления.

В следующем примере кода форма была построена с кнопочкой, которая была названа MyButton и элементол управления ToolTip, названным MyToolTip, which provides a ToolTip property.

//И вот так просто и наивно значение свойства кнопочки не получить,

//поскольку это свойство не родное!

//This is an example of code that is NOT CORRECT!

string myString;

myString = MyButton.ToolTip;

Подобное обращение пиведёт к ошибке ещё на стадии компиляции. А вот как надо обращаться к этому самому дополнительному свойству, установленному для кнопочки (просто форменное надувательство):

string myString;

myString = MyToolTip.GetToolTip(MyButton);

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

Пример…

using System;

using System.Drawing; using System.Collections;

using System.ComponentModel; using System.Windows.Forms;

namespace Rolls01

{

///<summary>

///Summary description for numPointsForm.

///</summary>

public class numPointsForm : System.Windows.Forms.Form

{

Form1 f1; int nPoints;

private System.Windows.Forms.TextBox numBox; private System.Windows.Forms.Button button1;

private System.ComponentModel.IContainer components;

private System.Windows.Forms.ToolTip toolTip;

// Ссылка на объект-представитель класса ErrorProvider private System.Windows.Forms.ErrorProvider errorProvider;

private string[] errMess;

public numPointsForm(Form1 f1Key)

{

f1 = f1Key; InitializeComponent(); errMess = new string[] {

"Больше двух тараканов!", "Целое и больше двух!"

};

}

///<summary>

///Clean up any resources being used.

///</summary>

protected override void Dispose( bool disposing )

{

if( disposing )

{

if(components != null)

180

Соседние файлы в предмете Языки программирования