Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Создание и завершение потоков

Поиск

В этом примере демонстрируется создание вспомогательного или рабочего потока и использование его для выполнения обработки параллельно с основным потоком. Также демонстрируется постановка одного потока в ожидание другого и корректное закрытие потока. Дополнительные сведения о многопотоковости см. в разделе Использование потоков.

В примере создается класс с именем Worker, содержащий метод DoWork, который будет выполняться рабочим потоком. Это, по существу, функция Main для рабочего потока. Выполнение рабочего потока начнется с вызова этого метода, а завершение происходит автоматически при возврате из метода. Метод DoWork выглядит следующим образом:

ß---

Класс Worker содержит дополнительный метод, который указывает, что DoWork должен выполнить возврат. Этот метод называется RequestStop и выглядит следующим образом:

public void RequestStop() { _shouldStop = true; }

Метод RequestStop присваивает элементу данных _shouldStop значение true. Так как этот элемент данных проверяется методом DoWork, это действие косвенно влияет на выполнение возврата DoWork, в результате чего рабочий поток закрывается. Однако следует обратить внимание на то, что DoWork и RequestStop будут выполняться различными потоками. DoWork выполняется рабочим потоком, а RequestStop выполняется основным потоком, поэтому _shouldStop элемент данных объявляется volatile следующим образом:

private volatile bool _shouldStop;

Ключевое слово volatile предупреждает компилятор о том, что элемент данных _shouldStop будет использоваться несколькими потоками и, следовательно, не следует делать предположений оптимизации о состоянии данного элемента.

 


The use of volatile with the _shouldStop data member enables you to safely access this member from multiple threads without the use of formal thread synchronization techniques, but only because _shouldStop is a bool. This means that only single, atomic operations are necessary to modify _shouldStop. If, however, this data member were a class, struct, or array, accessing it from multiple threads would likely cause intermittent data corruption. Consider a thread that changes the values in an array. Windows regularly interrupts threads in order to allow other threads to execute. Therefore, this thread could be stopped after assigning some array elements but before assigning others. Because the array now has a state that the programmer never intended, another thread that reads this array may fail.

Before actually creating the worker thread, the Main function creates a Worker object and an instance of Thread. The thread object is configured to use the Worker.DoWork method as an entry point by passing a reference to this method to the Thread constructor, like this:

Worker workerObject = new Worker(); Thread workerThread = new Thread(workerObject.DoWork);

At this point, although the worker thread object exists and is configured, the actual worker thread has not yet been created. This does not occur until Main calls the Start method:

workerThread.Start();

At this point the system initiates the execution of the worker thread, but it does so asynchronously to the primary thread. This means that the Main function continues to execute code immediately while the worker thread simultaneously undergoes initialization. To ensure that the Main function does not try to terminate the worker thread before it has a chance to execute, the Main function loops until the IsAlive property of the worker thread object is set to true:

while (!workerThread.IsAlive);

Next, the primary thread is stopped briefly with a call to Sleep. This ensures that the worker thread's DoWork function will execute the loop inside the DoWork method for several iterations before the Main function executes more commands:

Thread.Sleep(1);

After one millisecond elapses, Main signals to the worker thread object that it should terminate by using the Worker.RequestStop method introduced previously:

workerObject.RequestStop();

 


Использование ключевого слова volatile с элементом данных _shouldStop позволяет безопасно осуществлять доступ к этому элементу из нескольких потоков, не используя формальные методы синхронизации потоков, но только потому что _shouldStop является bool. Это означает, что только одинарные, атомарные операции необходимы для изменения _shouldStop. Если, однако, этот элемент данных являлся классом, структурой или массивом, использование его несколькими потоками скорее всего приведет к периодическому повреждению данных. Рассмотрим поток, изменяющий значения в массиве. Windows регулярно прерывает потоки, чтобы позволить другим потокам выполняться. Следовательно, этот поток будет остановлен после присваивания значений только некоторых элементов массива, до присваивания значений остальных. Так как текущее состояние массива не планировалось программистом, у другого потока может не удаться прочитать этот массив.

Перед тем, как фактически создать рабочий, функция Main создает объект Worker и экземпляр Thread. Объект потока конфигурируется для использования метода Worker.DoWork в качестве точки входа путем передачи ссылки на этой метод конструктору Thread следующим образом:

ß----

Хотя на этом этапе объект рабочего потока существует и уже настроен, фактический рабочий поток еще не создан. Этого не происходит до тех пор, пока Main не вызовет метод Start:

workerThread.Start();

На этом этапе система запускает выполнение рабочего потока, которое является асинхронным по отношению к основному потоку. Это означает, что функция Main продолжает немедленное выполнение кода, а рабочий поток одновременно проходит инициализацию. Чтобы функция Main не пыталась завершить рабочий поток до начала его выполнения, функция Main выполняется циклично до тех пор, пока свойству IsAlive рабочего потока не будет присвоено значение true:

while (!workerThread.IsAlive);

Далее основной поток кратковременно останавливается вызовом Sleep. Это гарантирует выполнение функцией DoWork рабочего потока цикла внутри метода DoWork в течение нескольких итераций до того, как функция Main выполнит несколько команд:

Thread.Sleep(1);

После того, как пройдет одна миллисекунда, Main передает объекту рабочего потока сигнал о завершении с использованием метода Worker.RequestStop, введенного ранее:

workerObject.RequestStop();

 


It is also possible to terminate a thread from another thread by using a call to Abort. This forcefully terminates the affected thread even if it has not completed its task and provides no opportunity for the cleanup of resources. The technique shown in this example is preferred.

Finally, the Main function calls the Join method on the worker thread object. This method causes the current thread to block, or wait, until the thread that the object represents terminates. Therefore Join will not return until the worker thread returns, thereby terminating itself:

workerThread.Join();

At this point only the primary thread executing Main exists. It displays one final message, and then returns, terminating the primary thread also.

Here is the complete example.

Example

using System; using System.Threading;   public class Worker { // This method will be called when the thread is started. public void DoWork() { while (!_shouldStop) { Console.WriteLine("worker thread: working..."); } Console.WriteLine("worker thread: terminating gracefully."); } public void RequestStop() { _shouldStop = true; } // Volatile is used as hint to the compiler that this data // member will be accessed by multiple threads. private volatile bool _shouldStop; }

 


Также можно завершить поток из другого потока, используя вызов Abort. При этом выбранный поток принудительно завершается, даже если его задача еще не выполнена, причем возможности очистки ресурсов не предоставляется. Рекомендуется использовать метод, показанный в этом примере.

В завершение, функция Main вызывает метод Join в объекте рабочего потока. Этот метод приводит к блокировке или ожиданию текущего потока до тех пор, пока не будет завершен поток, представляемый объектом. Следовательно, Join не возвращается до тех пор, пока не возвращается рабочий поток, таким образом завершая себя:

workerThread.Join();

На этом этапе существует только основной поток, выполняющий Main. Функция выводит на экран последнее сообщение и возвращается, завершая при этом основной поток.

Ниже приведен полный пример.

Пример

ß---


 

public class WorkerThreadExample { static void Main() { // Create the thread object. This does not start the thread. Worker workerObject = new Worker(); Thread workerThread = new Thread(workerObject.DoWork);   // Start the worker thread. workerThread.Start(); Console.WriteLine("main thread: Starting worker thread...");   // Loop until worker thread activates. while (!workerThread.IsAlive);   // Put the main thread to sleep for 1 millisecond to // allow the worker thread to do some work: Thread.Sleep(1);   // Request that the worker thread stop itself: workerObject.RequestStop();   // Use the Join method to block the current thread // until the object's thread terminates. workerThread.Join(); Console.WriteLine("main thread: Worker thread has terminated."); } }

Here is the output:

main thread: starting worker thread... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: terminating gracefully... main thread: worker thread has terminated

 

ß----

 

 

Ниже приведены выходные данные:

 

 

ß-------




Поделиться:


Последнее изменение этой страницы: 2017-01-19; просмотров: 220; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.216.208.243 (0.007 с.)