简体   繁体   中英

Make access to unsigned char thread-safe (atomic)

I am well aware that similar questions have been asked before and I'm also aware, that the operation is most likely not atomic at all, but I'm still asking out of idle curiosity and in hope that there's some way to MAKE it atomic.

The situation: Inside a struct , there's an unsigned char variable called Busy . (It can be moved out of there and stand on its own though).

This variable Busy is modified by two concurrent threads, one who sets bits on scheduling and one who clears them upon completion of the scheduled action.

Right now, the scheduling looks like this:

while(SEC.Busy&(1 << SEC.ReqID))
    if(++SEC.ReqID == 5) SEC.ReqID = 0;
sQuery.cData[2] = SEC.ReqID;

while the clearing of the bitmaks looks like this:

SEC.Busy &= ~(1 << sQuery->cData[2]);

cData[2] basically carries the information about which slot is used over the network and comes back via a callback in another thread.

Now the question: How can I make sure that SEC.Busy (which is the only variable in this troubled situation) doesn't get torn apart by two threads trying to alter it at the same time without using a mutex, critical section or anything of the likes if possible?

I've also tried assigning the content of SEC.Busy to a local variable, alter that and then write the variable back, but unfortunately this operation also doesn't seem atomic.

I'm using Borland C++ Builder 6 at the moment, though a GCC solution would also be just fine.

Thank you very much.

C++03 (nor C99) does not say anything about atomicity at all. Assignment is atomic (= everybody sees either the old or the new value) on most platforms, but because it is not synchronized (= anybody may see the old value after they saw new value for other updates) on any, it is useless anyway. Any other operation like increment, set bit and such is likely not even atomic.

C++11 defines std::atomic template , which ensures both atomicity and synchronization, so you need to use it. Boost provides compatible implementation for most C++03 compilers and gcc has had built-in support since 4.2 , which is being replaced by more advanced support needed by C++11 in gcc 4.7

Windows API had "Interlocked operations" since long ago. Unix alternative required assembly (which several libraries provided) before introduction of the gcc __sync function.

There are three potential problems when accessing shared data from multiple threads. First, you might get a thread switch in the middle of a memory access that requires more than one bus cycle; this is called "tearing". Second, each processor has its own memory cache, and writing data to one cache does not automatically write to other caches, so a different thread can see stale data. Third, the compiler can move instructions around, so that another processor might see a later store to one variable without seeing a preceding store to another one.

Using a variable of type unsigned char almost certainly removes the first one, but it doesn't have any effect on the other two.

To avoid all three problems, use atomic<unsigned char> from C++11 or whatever synchronizing techniques your compiler and OS provide.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM