What is transactional memory and how is it useful?
As multi-core processors become more and more common, the ability to write programs using all available processors becomes more and more important. Let’s look at why existing widely used tools for writing programs for multi-core processors are not a good enough solution to what transactional memory is and how it solves this problem.
As a rule, programs (if they are not written in a purely functional language) have variable memory areas in which these programs are located. If the program has several control flows that work with this data, it is important that access to them be structured so that there are no problems with parallel access, such as reading a memory area that is being written in parallel from another stream, or writing from two threads at the same time.
The most common means of synchronizing data access in imperative language programs is blocking. Before accessing the data, a lock must be taken on them, if the lock is already taken, the thread waits for the moment when the lock is released. Thus, we can be sure that more than one stream is never accessed by the same data. Unfortunately, there is a problem. I want many threads to work at the same time. Therefore, it would be logical to structure the program so that for each more or less independent part of the program data its own lock is made. But, when we have many locks, deadlocks are possible: in the case when the locks are taken in a different order. To deal with them, you need to take locks in the same order. For this, for each method you need to understand what locks can be taken by those who call it, and what locks can be taken during this call, and make sure that the lock order is always the same. It will make it work very hard, and even if the program works, inaccurate adding of a call in the wrong place can lead to deadlock.
What could be an alternative to locks? One solution to the problem of access to shared data is transactional memory. Transactional memory allows you to work with data using transactions similar to database transactions. Transactions are performed as if the current transaction is the only operation on the current data. At the end of the transaction, it looks for conflicts with other transactions. If they were not (the most likely option), the change is accepted, if not, transatzia is repeated again. In programs that have a large number of threads working with different areas of memory, such a scheme will work very well: conflicts will be rare, and the degree of parallelism will be high. Unfortunately, there are some disadvantages:
Currently, there are a fairly large number of libraries (for example, MultiVerse for Java) that allow you to use this approach in existing programming languages, as well as some languages (Fortress, Clojure) that support transactional memory directly.
As a rule, programs (if they are not written in a purely functional language) have variable memory areas in which these programs are located. If the program has several control flows that work with this data, it is important that access to them be structured so that there are no problems with parallel access, such as reading a memory area that is being written in parallel from another stream, or writing from two threads at the same time.
The most common means of synchronizing data access in imperative language programs is blocking. Before accessing the data, a lock must be taken on them, if the lock is already taken, the thread waits for the moment when the lock is released. Thus, we can be sure that more than one stream is never accessed by the same data. Unfortunately, there is a problem. I want many threads to work at the same time. Therefore, it would be logical to structure the program so that for each more or less independent part of the program data its own lock is made. But, when we have many locks, deadlocks are possible: in the case when the locks are taken in a different order. To deal with them, you need to take locks in the same order. For this, for each method you need to understand what locks can be taken by those who call it, and what locks can be taken during this call, and make sure that the lock order is always the same. It will make it work very hard, and even if the program works, inaccurate adding of a call in the wrong place can lead to deadlock.
What could be an alternative to locks? One solution to the problem of access to shared data is transactional memory. Transactional memory allows you to work with data using transactions similar to database transactions. Transactions are performed as if the current transaction is the only operation on the current data. At the end of the transaction, it looks for conflicts with other transactions. If they were not (the most likely option), the change is accepted, if not, transatzia is repeated again. In programs that have a large number of threads working with different areas of memory, such a scheme will work very well: conflicts will be rare, and the degree of parallelism will be high. Unfortunately, there are some disadvantages:
Currently, there are a fairly large number of libraries (for example, MultiVerse for Java) that allow you to use this approach in existing programming languages, as well as some languages (Fortress, Clojure) that support transactional memory directly.