How low do you go before something in itself becomes safe?

I thought how deep in everything you need to go before something is automatically thread safe?

Quick example:

int dat = 0; void SetInt(int data) { dat = data; } 

.. Will this method be considered thread safe? Of course, I wrap all my typing methods in mutex'es, but every time I do this, I can't help but think that it is useless. I think all this breaks down to the assembly that the compiler generates? When can threads infiltrate code? For assembly instructions or a code line? Can a thread be interrupted when setting up or destroying a method stack? Can an instruction like i ++ be considered thread safe, and if not, what about ++ i?

There are many questions here - and I do not expect a direct answer, but some information on this issue will be wonderful :)

[UPDATE] Since it is now clear to me (thanks for you guys, etc.) that only atomic-guarenteed material in the stream is an assembly instruction, I know, I thought: what about mutex- and semaphore-wrapperclasses? Such classes usually use methods that make calls - and custom semaphores that usually use some kind of internal counter cannot be encoded as atomic / thread safe (whatever you call, if you know what I mean, I don’t I care: P)

+7
c ++ multithreading thread-safety
source share
10 answers

considerations:

1) compiler optimization - do dates exist even as you planned? If this is not “externally observed” behavior, the abstract C / C ++ machine does not guarantee that the compiler will not optimize it. Your binary may not have a "dat", but instead you can write to a register, and threads will / may have different registers. Read the C / C ++ standard on an abstract machine or just google for “volatile” and explore from there. The C / C ++ standard takes care of a single thread, many threads can easily stumble over such optimization.

2) nuclear storage. Anything that has a chance to cross word boundaries will not be atomic. Int-s usually, if you do not pack them in a structure that has, for example, characters, and uses directives to remove the add-on. But you need to analyze this aspect every time. Explore your google platform to “populate”. Keep in mind that different CPUs have different rules.

3) problems with multiple CPUs. You wrote "dat" on CPU0. Will the change be visible even on CPU1? Or do you just write a local registry? Cache? Are the caches that connect you to your platform? Is access to a specific access guaranteed? Read on the "weak memory model." Gogle for "memory_barriers.txt Linux" is a good start.

4) use case. Do you intend to use "dat" after assignment - synchronized? But this, I think, is obvious.

Usually, "thread safety" does not go beyond guaranteeing that the function will work if it is called from different threads at the same time, but these calls should not be interdependent, that is, they do not exchange data regarding this call. For example, you call malloc () from thread1 and thread2, and both of them get memory, but they do not have access to other memory.

A counter example would be strtok (), which is not thread safe and will be interrupted even when calls are unconnected.

Once your threads start talking to each other over data, normal thread safety does not guarantee much.

+4
source share

In the general case, a thread context switch can occur at any time, between any two assembler language instructions. The CPU does not fully know how the assembler language maps to your source code. In addition, with multiple processors, other instructions can be executed on a different CPU core at the same time.

Having said that, in the example in which you pointed the processor-sized word to the memory location, it is usually an atomic operation. This means that from the point of view of the observer (another stream), the task has either not yet begun, or has been completed. Between the intermediate state no.

Multiprocessing has many subtleties, so it’s good to know about the capabilities of the equipment and the operating system in which you work.

+3
source share

The condition of the thread may vary between any two machine instructions. If the computer can perform the assignment in one machine instruction, then the assignment must be thread safe on a single processor machine. In general, it is unsafe to assume that the results of the calculation on the right side of the assignment can be calculated and stored at the location indicated by the left side of the task in one command. On some processor, there may be no instruction for copying memory to memory, and at first the data can be loaded into the register. If the context switch occurs between load and store instructions, then the result of the assignment is undefined (not thread safe). This is one of the reasons why most instruction sets contain an atomic test and a given operation, which allows you to use a memory cell as a lock. This allows other threads to check the availability of the lock and wait until it is received.

In your case, I’m not sure if it is important that the operation is completed in a thread-safe manner at the hardware level, since the result of several competing threads performing the assignment will simply be that one of them completes the last save and “win”. If you performed any calculations on the right side, however, it is connected with a calculation that used more than one variable, then I would definitely put it in the critical section, since you would like the results of the calculation to correspond to the state of these variables at start of calculations. If not in the critical section, the variables could change their values ​​in the middle of the stream by another stream, and you could get a result that is impossible from any stream.

+3
source share

Assigning native data types (32 bits) is atomic on most platforms (including x86). This means that the assignment will be completed in full, and you do not run the risk of having a “half updated” dat variable. But this is the only guarantee you get.

I am not sure about the purpose of a dual data type. You can find it in the x86 specs or check if .NET gives any explicit guarantees. But in general, data types that are not “native size” will not be atomic. Even smaller ones, for example, bool, may not exist (because to write bool you may have to read the whole 32-bit word, overwrite one byte, and then write the entire 32-byte word again)

In general, threads can be interrupted between any two assembly instructions. This means that your code above is thread safe unless you are trying to read from dat (which, you could argue, makes it useless).

Atomicity and thread safety are not exactly the same. Thread safety is completely context-specific. Your dat assignment is atomic, so another thread reading the dat value will either see the old or the new value, but it will never be "in between." But this does not make it thread safe. Another thread can read the old value (say, the size of the array) and perform an operation based on this. But you can update it immediately after it reads the old value, possibly setting it to a lower value. Another thread can now access your new, smaller array, but believe that it has an old larger size.

i ++ and ++ i are also not thread safe, because they consist of several operations (read value, increment value, write value), and in general, everything that consists of both reads and records is not thread safe. Streams can also be interrupted when setting up a call stack to call a function, yes. After any assembler instruction.

+3
source share

The only way to ensure that something is automatically streaming is to make sure that there is no altered general state. That's why functional programming is gaining momentum these days.

So, if all of your threads share X, then you need to make sure X has not changed. Any variables that change must be local to this stream.

+2
source share

It is not thread safe, and it is not suitable for all situations.

Suppose the dat variable contains the number of elements in an array. Another thread starts scanning the array using the dat variable, and its value is cached. In the meantime, you are changing the value of the dat variable. Another thread again scans the array for some other operation. Does another thread use the old dat value or the new one? We do not know, and we cannot be sure. Depending on the compilation of the module, it may use the old cached value or the new value, any case is a problem. Some of them.

You can explicitly cache the value of the dat variable in another thread to get more predictable results. For example, if the dat variable contains a timeout value, and you only write this value, and another thread reads, then I do not see a problem here. Even so, you cannot say that it is thread safe !!!

+2
source share

Well, I don't think everything should be thread safe. Since both complexity and performance are important to ensure code security, you should ask yourself if the code should be thread safe before implementing anything. In many cases, you can restrict flow awareness to certain parts of your code.

Obviously, this requires some thought and planning, but it also writes secure stream code.

+1
source share

The increment of an operation is not safe for x86 processors because it is not atomic. In windows you need to call InterlockedIncrement functions. This function generates full memory. Alternatively, you can use tbb :: atomic from the library of built-in stream streaming (TBB) blocks.

0
source share

There is a lot of research in transactional memory.
Something similar to a database transaction, but a much finer grain.

Theoretically, this allows multiple threads to read / write, doing something that they like with the object. But all operations on the object are controlled by transactions. If a thread changes the state of an object (and completes its transaction), all other threads that have open transactions on the object will be rolled back and restarted automatically.

This is done at the hardware level, so the software does not need to participate in locking issues.

Good theory. I can’t wait until it becomes a reality.

0
source share

The above code is thread safe!

The main thing to look for is static variables (ieshared).

They are not thread safe unless the update is controlled by some kind of fixed mechanism, such as a mutex. The same, obviously, applies to any OS that has shared memory.

As long as your code has no static data, it will be thread safe by itself.

Then you need to check if any of the libraries or system calls you are using are thread safe. This is indicated in the documentation of most system calls.

-4
source share

All Articles