Библиотека knigago >> Литература по изданиям >> Самиздат, сетевая литература >> Высокая производительность Delphi (черновик перевода глав 1-2)


СЛУЧАЙНЫЙ КОММЕНТАРИЙ

# 1781, книга: Дырка от бублика
автор: Галина Михайловна Куликова

Галина Куликова в своем ироническом детективе «Дырка от бублика» увлекает читателя в мир забавных и непредсказуемых приключений. Главная героиня книги, Ярослава Акулова, - яркая и независимая журналистка, которая случайно оказывается втянута в расследование загадочного убийства. Вместе с новым знакомым, харизматичным следователем Андреем Державиным, она приступает к поиску преступника, ускользающего от правосудия самым неожиданным образом. Автор мастерски сочетает элементы детектива и иронии,...

СЛУЧАЙНАЯ КНИГА

Второй Шанс.  bezymnyjhomyak1
- Второй Шанс

Жанр: Альтернативная история

Год издания: 2017

Серия: Второй шанс [bezymnyjhomyak1]

Примож Габриэльчич - Высокая производительность Delphi (черновик перевода глав 1-2)

Высокая производительность Delphi (черновик перевода глав 1-2)
Книга - Высокая производительность Delphi (черновик перевода глав 1-2).  Примож Габриэльчич  - прочитать полностью в библиотеке КнигаГо
Название:
Высокая производительность Delphi (черновик перевода глав 1-2)
Примож Габриэльчич

Жанр:

Самиздат, сетевая литература, Учебники и самоучители по компьютеру, Литература ХXI века (эпоха Глобализации экономики), Любительские переводы, Pascal, Delphi, Lazarus и т.п., Параллельное и распределенное программирование

Изадано в серии:

неизвестно

Издательство:

Интернет-издательство «Stribog»

Год издания:

ISBN:

неизвестно

Отзывы:

Комментировать

Рейтинг:

Поделись книгой с друзьями!

Помощь сайту: донат на оплату сервера

Краткое содержание книги "Высокая производительность Delphi (черновик перевода глав 1-2)"

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

Читаем онлайн "Высокая производительность Delphi (черновик перевода глав 1-2)". [Страница - 104]

thread. Additional threads that are created from the code are called background threads.

I said before and I'll repeat it here—adding multithreading to your code can create more problems than it is worth. That's why you should know about the problems before you learn how to do multithreading.

The first rule in Delphi is always: Never access the UI from a background thread. This is so important that I'll repeat it again.

Never access the user interface from a background thread!

If the code is running in a background thread, and definitely needs to access the user interface, you have to use mechanisms that take a part of the code (an anonymous method) and execute it in the main thread. I'm talking about TThread.Synchronize and TThread.Queue.

The next problem appears when two threads are accessing the same data structure at the same time. It is not really surprising that you should not read from a TList when another thread is deleting elements from the same list, but it may shock you to know that problems can also appear when one thread is reading from a simple int64 variable while another thread is writing to it.

Don't think that the first example (simultaneous access to a list) is purely theoretical. I recently fixed a similar example in my code (yes, it happens to the best of us). One thread was reading from a TFileStream and sending data to a web server. Another thread calculated the progress, and in that calculation called the TFileStream.Size function (they were different threads because I was using the asynchronous mode of the WinHTTP API). In my defense, I know very well that it is not a good idea, but, well, mistakes happen.

What's the problem with calling Size? Its implementation. When you read from Size, the GetSize method (shown following) calls Seek three times. Imagine what happened when one thread read from a stream and another changed the current position at the same time:

function TStream.GetSize: Int64;


var


Pos: Int64;


begin


Pos := Seek(0, soCurrent);


Result := Seek(0, soEnd);


Seek(Pos, soBeginning);


end;

To solve such issues, we typically introduce locking. This technique creates a sort of barrier that allows only one thread at a time to enter a critical path through the code. Critical sections are the standard locking mechanism, but we can also use mutexes and semaphores, although both are slow and more useful when synchronizing multiple processes.

Delphi also offers two mechanisms that are slightly faster than critical sections—TMonitor and TSpinLock. In situations when threads are mostly reading from shared data and rarely writing to it, the badly-named TMultiReadExclusiveWriteSynchronizer can be used. You can also refer to it with an alias, which is much simpler to type—TMREWSync. It's just too bad that the implementation of this mechanism is terribly slow and that pure critical sections typically operate faster.

When you need to use locking, you should use TMonitor or TSpinlock.

Locking techniques introduce at least two problems to the program. They lead to slower operations, and they can result in deadlocks when improperly used. In a deadlocked program, one thread is waiting on another thread, which is waiting on the first one, and all operation stops.

An alternative to locking is interlocked (atomic) operations. They are faster than locking but only support a small range of operations—incrementing and decrementing a memory location, modifying a memory location, and conditionally modifying a memory location.

The latest variation, TInterlocked.CompareExchange, can be used to implement optimistic initialization. This mechanism can be used to safely create a shared object or interface. The latter is preferred as it will be safely destroyed at the right time.

Instead of using one of the synchronization techniques described previously, you can introduce communication mechanisms to the program. Instead of accessing a shared value, you can use data duplication (sending a copy of full or partial input data to each worker thread) and aggregation (putting partial results calculated in threads together). Each thread can then work on its own copy of data and doesn't have to use any locking to access it.

Communication is better than synchronization!

Standard techniques to implement messaging in a Delphi program are Windows messages, TThread methods Synchronize and Queue, and polling in connection with a thread-safe message queue, such as TThreadedQueue<T>.

The best way to find problems in a multithreaded application is to do stress-testing, repeating multithreaded operations multiple times, and testing your program on only one CPU core (Windows API SetProcessAffinityMask will help you with that). You'll be surprised how many problems can be found this way. If at all possible (the implementation may prevent you from doing this), you should also test the code by creating more worker threads/tasks than there are CPU cores in the computer.

Use automated tests as much as possible and you'll be able to trust your code more. Never trust the multithreaded code fully. It actually never fully works; it's just that the bugs are so deeply hidden that they almost never appear.

Working with parallel tools


There are multiple ways to implement multithreading in an application, and Chapter 6, Working with Parallel Tools, dealt with the most basic of them all—TThread. This class was introduced in Delphi 2 where it simply wrapped the Windows CreateThread API. Later, it was enhanced with additional methods and got support for other operating systems but, in essence, it stayed the same good old, stupid, clumsy TThread, which we all learned to love and hate.

Threads created with TThread can be used in two modes. In one, the code has full control over a TThread object—it can create it, tell it to terminate (but the object must observe that and wilfully terminate), and destroy it. In other modes, the code just creates a thread that does its job, terminates it, and is automatically destroyed. The former is more appropriate for service-like operations. You start a thread that then responds to requests, and performs some operations as a response to those requests. When you don't need it anymore, the code destroys the thread. The latter is more appropriate for background calculations. You start a thread, the thread executes some calculation, notifies the main thread of the result, and exits.

Working with the basic TThread object is quite clumsy, so in this chapter I presented a way to combine TThread and a communication channel. While it is simple to send messages from a background thread to the main thread (and I described four such mechanisms in the previous chapter), sending messages to a thread is hard.

I introduced a DHPThreads unit, which implements the TCommThread class representing a thread with a two-way communication channel, which can transfer TValue records to and from the thread. To use this thread, you had to write its Execute method in a fairly specific way, which limits its use.

To simplify the code, I introduced a more capable TCommTimerThread that shows how to work with threads on a more abstract level. You no longer override the Execute method as with the standard TThread mechanism, but merely introduce simple event handlers. The base class runs the Execute loop, processes messages, handles timers, and calls event handlers when appropriate. One event handler processes messages while another handles timer events.

If you approach multithreading that way, you move from the standard big block of code way to a modular event-driven architecture. This simplifies the programming logic, reduces the quantity of shared data, and --">

Оставить комментарий:


Ваш e-mail является приватным и не будет опубликован в комментарии.