Concurrency (Multithreading)Concurrency (Multithreading)

This page is part of an incremental translation project. Body text not yet translated to Turkish falls back to English even in TR mode.

TROIA programlama dili, platformun 8.02 sürümünden itibaren concurrency (multi-threading) computing'i destekler. Bu bölüm, TROIA'da concurrent programming'i ve ilgili konseptleri tanıtmayı amaçlar.TROIA programming language supports concurrency (multi-threading) computing onwards 8.02 version of the platform. This section aims to introduce you concurrent programming and related concepts in TROIA

Concurrency Nedir?What is Concurrency?

Concurrency, aynı anda birden fazla operation gerçekleştirmektir. Bir yazılım concurrent ise, birden fazla operation dizisinin çakışan zaman aralıklarında çalıştığı anlamına gelir. Concurrent programming'de, birden fazla operation'ın eşzamanlı gerçekleştirilmesi garanti edilmez, bu yüzden belirli bir sürede thread'lerden biri diğerlerinin operation'larını gerçekleştirmesini bekleyebilir.Concurrency is, performing more than one operation at the same time. If a software is concurrent it means that multiple sequences of operations are run in overlapping periods of time. In concurrent programming, it is not guaranteed that multiple operations are performed simultaneously, so in a specific period, one of the threads can wait for others to perform their operations.

Concurrent programming'in en önemli konseptlerinden biri, sensitive variable ve function'lar gibi birden fazla thread'in paylaştığı "common resource"lardır. Birden fazla thread, bu shared resource'ları değiştirmek ve okumak için yarışır. Bu yüzden, programcılar tüm thread'leri bu shared resource'ları güvenli ve emniyetli bir şekilde kullanacak şekilde organize etmelidir. Concurrent programming (multi-threading) ve ilgili konseptler hakkında daha fazla bilgi için ilgili makale ve kitaplara bakınız.One of the most important concepts of concurrent programming is “common resources” that multiple threads share like sensitive variables and functions etc. Multiple threads race to modify and read these shared resources. Therefore, programmers have to organize all threads to use this shared resources in a safe and secure way. For more information about concurrent programming (multi-threading) and related concepts, see related articles and books.

TROIA'da Concurrency KonseptleriConcurrency Concepts in TROIA

Çoğu programlama dili gibi TROIA da, TROIA thread'leri sayesinde concurrency'i destekler. Bir TROIA thread'i, TROIA runtime tarafından bağımsız olarak yönetilebilen bir tür program instruction dizisidir. Geleneksel troia programlamada, tüm instruction'ların çalıştığı yalnızca bir thread vardır. Concurrent programming yaklaşımında, programcılar belirli bir task'ı gerçekleştiren birden fazla thread tanımlayabilir. TROIA'da thread'ler, bir veya daha fazla thread içerebilen thread group'larında yönetilebilir.Like most programming languages TROIA, supports conccurrency thanks to TROIA threads. A TROIA thread is a kind of sequence of program instructions that can be managed independently by TROIA runtime. In conventional troia programming, there is only one thread that all instructions executed on. In concurrent programming approach, programmers can define multiple threads that performs a specific task. In TROIA threads can be managed in thread groups that can contain one or more threads.

Daha önce belirtildiği gibi shared resource'lar programcılar tarafından yönetilmesi gereken önemli bir sorun oluşturur, ancak TROIA'da SYS_STATUS, SYS_STATUSERROR gibi bazı variable'lar tasarım gereği transaction scope'unda global'dir. Bu, transaction scope'undaki thread'ler için bir tür engel veya kısıtlamadır. Bu kısıtlamalar nedeniyle, tek bir transaction içinde birden fazla thread oluşturmak mümkün değildir, bu yüzden her thread'in kendi transaction'ı olmalıdır. Yeni bir thread başlatmak ve bu thread içinde bazı task'lar gerçekleştirmek için CALL TRANSACTION komutu kullanılır. Bu, trace dosyaları gibi diğer common resource'ları ayırmak için de kullanışlıdır.As mentioned before shared resources consist an important problem that must be managed by programmers, however in TROIA some variables like SYS_STATUS, SYS_STATUSERROR are global in transaction scope, by design. This is kind of obstacle or limitation for threads in transaction scope. Because of this limitations, it is not possible to create multiple threads in a single transaction, so each thread must have its own transaction. To start a new thread and perform some tasks in this thread CALL TRANSACTION command is used. This is also useful for separating other common resources like trace files etc.

Bir diğer shared resource, database connection'dır; önceki bölümlerde belirtildiği gibi database connection tüm transaction'lar arasında paylaşılır. Bu, concurrent bir TROIA application yazmak için bir başka sorundur. Bu sorunu aşmak için, alternatif thread'ler tarafından çalıştırılan transaction'ların kendi database connection'larına sahip olması gerekir. Dedicated database connection'larla bir transaction açmak için, transaction'ları yeni thread olarak çağırırken CALL TRANSACTION komutunun EXCLUSIVEDB keyword'ü kullanılmalıdır.Another shared resource is database connection, as mentioned in previous sessions database connection is shared between all transaction. This is another problem to write an concurrent TROIA application. To overcome this problem transactions that is executed by alternative threads must have their own database connection. To open a transaction with dedicated database connections, EXCLUSIVEDB keyword of CALL TRANSACTION command must be used while calling transactions as new threads.

Şimdi, tüm bu konseptleri örneklerle detaylıca ele alalım.Now, lets discuss all these concepts in detail with examples.

Thread Group'larıThread Groups

Thread group, birkaç thread'i tek bir grupta kategorize etmek için bir tür label'dır. Programcıların bir grup içindeki tüm thread'ler için ortak bir davranış oluşturmasına olanak tanır. Task'ın parçalarını gerçekleştiren dört thread'iniz olduğunu ve başka bir task'ı çalıştırmak için bu dört thread'in tamamının bitmesini beklemek istediğinizi varsayın. Bu durumda, her birinin state'ini kontrol etmeden hepsini beklemek için tüm thread'lerinizi aynı label ile etiketlemelisiniz.Thread group is a kind of label to categorize a couple of threads in a single group. It allows programmers to create a common behavior for all threads in a group. Assume that you have four threads that performs parts of task and you want to wait for all these four threads to finish to run another task. In this case, you must label all your threads with same label to wait all of them without checking state of each thread.

İşte thread group'larının daha karmaşık bir örneği, lütfen pseudocode'un davranışını anlamaya çalışınHere is a more complex example of thread groups, please try to understand the behavior of pseudocode

- start thread on group1
- start thread on group1

- start thread on group2
- start thread on group2

- wait until group1 ends
- do something (1)
- wait until group2 ends
- do something (2)

Bu durumda, sistem ilk grupta iki thread ve başka bir grupta iki ek thread başlatır. Ardından, ilk "do something" block'unu çalıştırmak için ilk grubun bitmesini bekler. İlk iki thread bittiğinde, ikinci grup hâlâ çalışırken program ilk do something block'una geçer. İkinci gruptaki thread'ler bittikten sonra ikinci do something block'una geçer.In this case, system starts two threads on first group and additional two threads in another group. Then, waits first group to end to do first do somehing block. When first two threads ends, program moves to fist do something block while second group is still running. After threads on second group ends it moves to second do somehing block.

Her troia thread'inin bir grup ismi olmalıdır, ancak bir grupta yalnızca bir thread tanımlamak da mümkündür. Yani, troia thread'lerini tek tek yönetmek de mümkündür.Each troia thread must have a group name, but it is possible to define only one thread in a group. So, it is also possible to manage troia threads individually.

TROIA Thread'leri OluşturmaCreating TROIA Threads

Yeni bir TROIA thread'i başlatmak için, önce CALL TRANSACTION komutunun INSERVER varyasyonu ile server'da yeni bir transaction çağırmanız gerekir. Server'da bir transaction çağırmak, transaction'ı açar, BEFORE ve AFTER event'leriyle ilk dialog'unu başlatır ve CALL TRANSACTION komutunda en az bir parametre varsa TRANSCALLED function'ını çağırır ve verilen parametrelere göre TIMEPROCESS event'ini çağırır. Server'da bir transaction çağırma hakkında daha fazla bilgi için ilgili bölümü inceleyin.To start a new TROIA thread, firstly you need to call a new transaction in server with INSERVER variation of CALL TRANSACTION command. Calling a transaction in server, opens the transaction, starts first dialog it with BEFORE and AFTER events and then calls TRANSCALLED function if there is at least one parameter and calls TIMEPROCESS event due to given parameters on CALL TRANSACTION command. For more information about calling a transaction in server please review related section.

Bir transaction'ı yeni bir thread ile çağırmak için, çağrılan transaction için ONTHREADGROUP keyword'üyle bir thread group belirtmeli ve first ve second database connection'ları için dedicated database connection kurmak amacıyla EXCLUSIVEDB parametresini kullanmalısınız. Bu iki database connection hakkında daha fazla bilgi için ilgili bölüme bakınız. İşte bir thread'de transaction çağırmak için basitleştirilmiş bir syntax.To call transaction with a new thread you must indicate and thread group for the called transaction with the ONTHREADGROUP keyword and EXCLUSIVEDB parameter to establish dedicated database connections for first and second database connections. For more information about these two database connections please see related section. Here is a simplified syntax for calling transaction in a thread.

CALL TRANSACTION RDTT06 INSERVER [({inputparams})] ONTHREADGROUP {tgroupid} EXCLUSIVEDB;

Transaction çağrısı için bir thread group tanımlamazsanız, bu geleneksel single thread call transaction olarak kabul edilir ve yeni bir thread tarafından gerçekleştirilmez. EXCLUSIVEDB parametresi zorunlu değildir ama thread'lerinizde bazı database operation'larına ihtiyacınız varsa, bazı ince sorunlara neden olabilir, bu yüzden bir thread'de çağrılan transaction için dedicated database connection kurmak önerilir.If you don’t define a thread group for transaction call, it is considered as an conventional single thread call transaction and does not performed by a new thread. EXCLUSIVEDB parameter is not a must but if you need some database operations in your threads, it can cause some subtle problems, so it is recommended to use establish dedicated database connections for transaction called in a thread.

Kendi thread'iyle bir transaction çağırdıktan sonra kodunuz transaction çağrısının bitmesini beklemeden bir sonraki satıra akar; başka bir task gerçekleştirmek için bir thread group'taki tüm transaction'ların bitmesini beklemeniz gerekiyorsa JOINTHREADGROUP komutunu kullanmalısınız. İşte syntax'ı:After calling a transaction with its own thread your code flows to the next line without waiting end of transaction call, if you need to wait all transactions in a thread group to end for performing another task you must use JOINTHREADGROUP command. Here is the syntax for it:

JOINTHREADGROUP {tgroupid};

Client'a dönmeden önce tüm thread group'larını beklemek zorunludur, aksi halde thread lifecycle'ı ile ilgili bazı ince sorunlarla karşılaşmak mümkündür.Waiting for all thread groups before returning to client is must, otherwise it is possible to encounter some subtle problems about threads lifecycle.

Aşağıda tüm bunları bir araya getiren basit bir örnek var. Bu örnekte; THREADID özel bir variable veya reserved word değildir, sadece her transaction için ayırt edici bir bilgi geçirmek için bir function parametresidir.Here is a simple example below puts all of them together. In this example; THREADID is not a special variable or reserved word, it is just a function parameter to pass a distinctive information for each transaction.

OBJECT:
  STRING THREADID;

TRACEON;

THREADID = 'T1';
CALL TRANSACTION RDTT06 INSERVER (THREADID) ONTHREADGROUP 'TG1' EXCLUSIVEDB;

THREADID = 'T2';
CALL TRANSACTION RDTT06 INSERVER (THREADID) ONTHREADGROUP 'TG1' EXCLUSIVEDB;

THREADID = 'T3';
CALL TRANSACTION RDTT06 INSERVER (THREADID) ONTHREADGROUP 'TG1' EXCLUSIVEDB;

JOINTHREADGROUP 'TG1';

TRACEOFF;

RDTT06 transaction'ının ilk dialog'unun TRANSCALLED function'ının sadece bir "DELAY 10000;" (on saniye uyuma) olduğunu varsayalım. Bu kodun tamamı yaklaşık on saniye sürer. Ve tüm thread'ler bittiğinde TRACEOFF komutu çalışır. Ama ONTHREADGROUP'u kaldırırsanız kod en az otuz saniye sürer, çünkü tüm call transaction'lar sıralı olarak çalıştırılır.Assume that TRANSCALLED function of RDTT06 transaction’s first dialog is just a “DELAY 10000;” (sleep for ten seconds). All this code takes about ten seconds. And TRACEOFF command runs when all threads end. But if you remove ONTHREADGROUP code takes at least thirty seconds, because all call transactions executed sequentially.

Bazı durumlarda; JOINTREADGROUP komutundan önce, programcıların thread group'un hâlâ çalışıp çalışmadığını kontrol etmesi gerekebilir. ISTHREADGROUPALIVE() function'ı thread groupid'yi alır ve verilen thread state'ini gösteren bir boolean sonuç döndürür.In some cases; before the JOINTREADGROUP command, programmers may need to check thread group state whether it is still working. ISTHREADGROUPALIVE() function gets thread groupid and returns a boolean result that shows given thread state.

Semaphore TanımlamaDefining Semaphores

Bazı durumlarda, belirli bir code block'u thread'ler arasında bazı shared resource'lara erişebilir. Bu tür thread sensitive block'lar yalnızca bir thread tarafından gerçekleştirilmeli ve diğer thread'ler aktif thread işlemlerini bitirene kadar beklemelidir. Daha iyi anlamak için, thread'ler arasında bir tür bayrak kapma oyunu düşünebilirsiniz. Bir thread bayrağı alır ve belirli operation'ı gerçekleştirir, ardından bayrağı bırakır; alma ve bırakma arasındaki sürede, aynı bayrağı bekleyen diğer thread'ler bir sonraki alıcı olmayı bekler. Bu bayrak benzeri yapılara concurrent programming'de semaphore denir. TROIA, thread safe programming block'ları implement etmek için semaphore'ları destekler.In some cases, a particular code block can access some shared resources between threads. These kind of thread sensitive blocks must be performed by only one thread and other threads must wait until active thread finishes its operations. For better understanding, you can assume a kind of capture the flag game between the threads. A thread acquires the flag and performs the particular operation and then releases the flag, the period between acquiring and releasing, other threads which are waiting for same flag just waits to be the next acquirer. This flag like structures is called semaphore on cuncurrent programming. TROIA supports semaphores for implementing thread safe programming blocks.

TROIA'da; thread sensitive programming block'ları ACQUIRESEMAPHORE ve RELEASESEMAPHORE komutlarıyla sınırlandırılır:In TROIA; hread sensitive programming blocks are bounded with ACQUIRESEMAPHORE and RELEASESEMAPHORE commands:

ACQUIRESEMAPHORE {semaphoreid} [SCOPE SERVER | SYSTEM]
RELEASESEMAPHORE {semaphoreid} [SCOPE SERVER | SYSTEM]

Semaphore tanımlarının scope'u application server geneli veya system geneli (tüm application server instance'ları arasında) olabilir. Düzenli bir concurrent application'da server geneli bir semaphore yeterlidir ve iyi çalışır. Ama bazı durumlarda programcıların application server'lar arasında sensitive bir data'yı kontrol etmek için system geneli semaphore'lara ihtiyacı olabilir. Komutların SCOPE varyasyonu semaphore tanımının scope'unu belirtir.Scope of semaphore definitions can be application server wide or system wide (between all application server instances). In a regular concurrent application a server wide semaphore enough and works well. But in some cases programmers may need system wide semaphores to control a sensitive data between application servers. SCOPE variation of the commands states the scope of semaphore defintion.

İşte TROIA'da semaphore alma ve bırakma hakkında bir örnek. Bunun bir dialog'un TRANSCALLED method'u olduğunu ve bu dialog'un, aşağıdaki örnek gibi kendi thread'iyle üç kez çağrılan bir transaction'ın start dialog'u olduğunu varsayın. Her CALL TRANSACTION komutu aynı transaction için bir transaction çağırır. ACQUIRESEMAPHORE ve RELEASESSEMAPHORE arasındaki code block'u atomik bir block'tur ve bu block'un içinde yalnızca bir thread olabilir. (LOGDEBUG komutu sadece verilen text'i application server console'una yazdırır)Here is an example about acquiring and releasing semaphores in TROIA. Assume that this is the TRANSCALLED method of a dialog and this dialog is the start dialog of a transaction that is called three times with its own thread like the example below. Each CALL TRANSACTION command calls an transaction for the same transaction. The code block between ACQUIRESEMAPHORE and RELEASESSEMAPHORE is an atomic block and only one thread can be inside of this block. (LOGDEBUG command just prints given text to application server console)

OBJECT:
 INTEGER NINDEX,
 INTEGER PERIOD,
 STRING PTEXT;

NINDEX = 5;
ACQUIRESEMAPHORE 'mysemaphore' SCOPE SYSTEM;

WHILE NINDEX > 0
BEGIN
        NINDEX = NINDEX -1;
        PTEXT = THREADID + ' -> ' + NINDEX;
        LOGDEBUG PTEXT;
        PERIOD = RAND() % 1000;
        DELAY PERIOD;
ENDWHILE;

RELEASESEMAPHORE 'mysemaphore' SCOPE SYSTEM;

Bu function, verilen THREADID parametresini (ki bu sadece transaction ve thread'i tanımlayan bir text'tir) ve her thread için 5'ten 1'e kadar NINDEX numarasını yazdırır. Kodun semaphore block'u içerdiği ve içermediği durumlar için çıktılar arasındaki farkları tartışmaya ve bulmaya çalışın.This function prints given THREADID parameter (which is just a text to define transaction and thread) and the NINDEX number from 5 to 1 for each thread. Please discuss and try to find the differences between the outputs, for the cases that code contains and does not contain semaphore block.