What feature corresponds to 'synchronized' in Java?
Asked Answered
S

5

51

synchronized in Java can guarantee thread-safety when accessing a shared object. What about C++?

Strander answered 25/3, 2011 at 7:25 Comment(4)
C++ says nothing about threads. You'll need to rely on library support.Geodetic
@DavidHeffernan: Since C++11, it does!Draconic
@Nawaz yes, have you seen the date on this post?Geodetic
@DavidHeffernan: Yes, I saw Mar 25 11 :PDraconic
V
53

Use the following in C++:

#include <mutex>

std::mutex _mutex;

void f()
{
     std::unique_lock<std::mutex> lock(_mutex);
     // access your resource here.
}
Vaenfila answered 25/3, 2011 at 7:29 Comment(0)
A
9

Despite this question has been already answered, by the idea of this article I made my version of synchronized keyword using just standard library (C++11) objects:

#include <mutex>
#define synchronized(m) \
    for(std::unique_lock<std::recursive_mutex> lk(m); lk; lk.unlock())

You can test it like:

#include <iostream>
#include <iomanip>
#include <mutex>
#include <thread>
#include <vector>

#define synchronized(m) \
    for(std::unique_lock<std::recursive_mutex> lk(m); lk; lk.unlock())

class Test {
    std::recursive_mutex m_mutex;
public:
    void sayHello(int n) {
        synchronized(m_mutex) {
            std::cout << "Hello! My number is: ";
            std::cout << std::setw(2) << n << std::endl;
        }
    }    
};

int main() {
    Test test;
    std::vector<std::thread> threads;
    std::cout << "Test started..." << std::endl;

    for(int i = 0; i < 10; ++i)
        threads.push_back(std::thread([i, &test]() {
            for(int j = 0; j < 10; ++j) {
                test.sayHello((i * 10) + j);
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
            }
        }));    
    for(auto& t : threads) t.join(); 

    std::cout << "Test finished!" << std::endl;
    return 0;
}

This is just an approximation of synchonized keyword of Java but it works. Without it the sayHello method of the previous example can be implemented as the accepted answer says:

void sayHello(unsigned int n) {
    std::unique_lock<std::recursive_mutex> lk(m_mutex);

    std::cout << "Hello! My number is: ";
    std::cout << std::setw(2) << n << std::endl;
}
Antihalation answered 10/5, 2017 at 14:0 Comment(1)
This would be great if implemented as as a template, but I've found, with a few well known exception (e.g. assert), code implemented with _#define_s is difficult to debug.Alterable
D
6

There is no keyword in C++03 equivalent to synchronized in Java . But you can use Mutex to guarantee safety of thread.

Draconic answered 25/3, 2011 at 7:29 Comment(0)
R
2

C++ does not have built-in threading or synchronization (yet), you have to use libraries for that. Boost.Thread is a good portable library that is designed to be compatible with the proposed threading facilities in C++0x.

Riplex answered 25/3, 2011 at 7:29 Comment(0)
E
0

In C++, I've implemented a macro that lets you simply do the following in order to synchronize access to a function or method:

// Define a function we want to synchronize.
void synchronizedFunction()
{
    // Synchronize this function.
    synchronized;

    // We are now synchronized.
}

Here's how the macro is defined:

// Under the hood, we use a mutex to synchronize.
#include <mutex>

// Create a synchronized keyword.
#define CONCAT_INNER(a, b) a##b
#define CONCAT(a, b) CONCAT_INNER(a, b)
#define UNIQUE_NAME(base) CONCAT(base, __LINE__)
#define synchronized static std::mutex UNIQUE_NAME(syncMutex); std::unique_lock<std::mutex> syncLock(UNIQUE_NAME(syncMutex))

This works for me with all three popular compilers { Windows, Clang, GCC }. This macro creates a unique static mutex variable for the function, and immediately locks it within the scope of that function. It could also be used within any scope, not just a function scope.

You can try this test program to prove that it works:

// Under the hood, we use a mutex to synchronize.
#include <mutex>

// Create a synchronized keyword.
#define CONCAT_INNER(a, b) a##b
#define CONCAT(a, b) CONCAT_INNER(a, b)
#define UNIQUE_NAME(base) CONCAT(base, __LINE__)
#define synchronized static std::mutex UNIQUE_NAME(syncMutex); std::unique_lock<std::mutex> syncLock(UNIQUE_NAME(syncMutex))

// For our test program below.
#include <iostream>
#include <thread>

// Define a function we want to synchronize.
void synchronizedFunction()
{
    // Synchronize this function.
    synchronized;

    // Print something, then sleep for 1 second, then print something else.
    // If we're synchronized, these two messages will appear back-to-back.
    // If we're not synchronized, these messages display in an uncontrolled fashion.
    std::cout << "Thread " << std::this_thread::get_id() << " is entering." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Thread " << std::this_thread::get_id() << " is exiting." << std::endl;
}

// A simple test program that will access our synchronized function from 2 asynchronous threads.
int main()
{
    // Construct a new thread and have it call our synchronized function.
    std::thread newThread(synchronizedFunction);

    // In our main thread, also call our synchronized function.
    synchronizedFunction();
    
    // Join both threads before exiting.
    newThread.join();
    
    // Exit.
    return 0;
}

When you run this program, you get the following output -- demonstrating that access to the function is serialized for each accessing thread. You will notice the one-second delay from when the function is entered and when the function has exited (both times). Because the function is now serialized, the test program will take approximately 2 seconds for this function to complete:

Thread 140737353824064 is entering.
Thread 140737353824064 is exiting.
Thread 140737257031424 is entering.
Thread 140737257031424 is exiting.

If you comment out the "synchronized;" statement, then you'll see that both threads enter the function almost simultaneously, producing something similar to the following output, or you may see text that stomps on top of itself. Because we are no longer synchronized, the test program will take approximately 1 second to complete instead of 2 seconds:

Thread 140737257031424 is entering.
Thread 140737353824064 is entering.
Thread 140737257031424 is exiting.
Thread 140737353824064 is exiting.
Elvaelvah answered 22/9, 2023 at 15:18 Comment(2)
While the macro idea is cool, I think it is too easy to abuse. From my experience, it is quite rare that you need some mutex in a single function. The mutex most of the time protects an object rather than a function, and needs to be locked at each access to the object, often at least in a getter and a setter. If you use synchronized macro in those getter and setter, you have undefined behavior again because we need to lock the same mutex. While the code looks protected, it is not.Piperpiperaceous
@prapin: For clarity, I have edited my answer to make clear that the context of my answer is to synchronize access to a single function or method. Keep in mind, however, that Java's "synchronized" keyword is primarily used in exactly the way that you dislike. If someone wanted to make an entire class instance thread safe, I agree with you that there are much better ways to achieve that in C++ (e.g. std::lock_guard<std::mutex>) than to try and emulate "synchronized", but the question's title is "What [C++] feature corresponds to 'synchronized' in Java?", and this is a good solution for that.Elvaelvah

© 2022 - 2024 — McMap. All rights reserved.