Including std::lock_guard in extra scope
Does is make sense to do something like putting a std::lock_guard
in an extra scope so that the locking period is as short as possible?
Pseudo code:
// all used variables beside the lock_guard are created and initialized somewhere else
...// do something
{ // open new scope
std::lock_guard<std::mutex> lock(mut);
shared_var = newValue;
} // close the scope
... // do some other stuff (that might take longer)
Are there more advantages besides having a short lock duration?
What might be negative side effects?
c++ locking mutex
add a comment |
Does is make sense to do something like putting a std::lock_guard
in an extra scope so that the locking period is as short as possible?
Pseudo code:
// all used variables beside the lock_guard are created and initialized somewhere else
...// do something
{ // open new scope
std::lock_guard<std::mutex> lock(mut);
shared_var = newValue;
} // close the scope
... // do some other stuff (that might take longer)
Are there more advantages besides having a short lock duration?
What might be negative side effects?
c++ locking mutex
add a comment |
Does is make sense to do something like putting a std::lock_guard
in an extra scope so that the locking period is as short as possible?
Pseudo code:
// all used variables beside the lock_guard are created and initialized somewhere else
...// do something
{ // open new scope
std::lock_guard<std::mutex> lock(mut);
shared_var = newValue;
} // close the scope
... // do some other stuff (that might take longer)
Are there more advantages besides having a short lock duration?
What might be negative side effects?
c++ locking mutex
Does is make sense to do something like putting a std::lock_guard
in an extra scope so that the locking period is as short as possible?
Pseudo code:
// all used variables beside the lock_guard are created and initialized somewhere else
...// do something
{ // open new scope
std::lock_guard<std::mutex> lock(mut);
shared_var = newValue;
} // close the scope
... // do some other stuff (that might take longer)
Are there more advantages besides having a short lock duration?
What might be negative side effects?
c++ locking mutex
c++ locking mutex
edited Jan 28 at 10:21
Angew
134k11258351
134k11258351
asked Jan 28 at 10:13
KabCodeKabCode
333314
333314
add a comment |
add a comment |
5 Answers
5
active
oldest
votes
Yes, it certainly makes sense to limit the scope of lock guards to be as short as possible, but not shorter.
The longer you hold a lock, the more likely it is that a thread will block waiting for that lock, which impacts performance as is thus usually considered a bad thing.
However, you must make sure that the program is still correct and that the lock is held at all times when it must be, i.e. when the shared resource protected by the lock is accessed or modified.
There may be one more point to consider (I do not have enough practical experience here to speak with certainty). Locking/releasing a mutex can potentially be an operation with nontrivial performance costs itself. Therefore, it may turn out that keeping a lock for a slightly longer period instead of unlocking & re-locking it several times in the course of one operation can actually improve overall performace. This is something which profiling could show you.
add a comment |
There might be a disadvantage: you cannot protect initializations this way. For example:
{
std::lock_guard<std::mutex> lock(mut);
Some_resource var{shared_var};
} // woops! var is lost
You have to use assignment like this:
Some_resource var;
{
std::lock_guard<std::mutex> lock(mut);
var = shared_Var;
}
Which can be not as good, you know, since for some types, dummy initialization (I intentionally avoided the term "default initialization") and then assignment is less efficient than directly initialization. (I intentionally avoided the term "direct initialization") Furthermore, in some situations, you cannot change the variable after initialization. (e.g. const
ones)
@P i pointed out this solution:
// use an immediately-invoked temporary lambda
Some_resource var {
[&] {
std::lock_guard<std::mutex> lock(mut);
return shared_var;
} () // parentheses for invoke
};
This way, with return value optimization, which is implemented well and properly by almost all popular modern compilers, and which is mandated as of C++17, you can do exactly what is expected.
1
Hi, what's wrong? I am willing to improve :)
– L. F.
Jan 28 at 10:28
3
You could use immediately invoked lambda
– user32434999
Jan 28 at 10:31
1
@KabCode It is as the scope is only due to the scheme mentioned in the question
– user32434999
Jan 28 at 10:33
2
Some_resource var{ [&]() { std::lock_guard<std::mutex> lock(mut); return shared_var; }() };
– user32434999
Jan 28 at 10:43
1
@L.F. I also think shared_var deserves a const
– user32434999
Jan 28 at 10:49
|
show 10 more comments
Yes, it makes sense.
There are no other advantages, and there are no side-effects (it is a good way to write it).
An even better way, is to extract it into a private member function (if you have an operation that is synchronized this way, you might as well give the operation its own name):
{
// all used variables beside the lock_guard are created and initialized somewhere else
...// do something
set_var(new_value);
... // do some other stuff (that might take longer)
}
void your_class::set_value(int new_value)
{
std::lock_guard<std::mutex> lock(mut);
shared_var = new_value;
}
Good addition. This will help to keep the code clean. Caveat might be if you have to set this variable often then the costs of creating new locks might be more expensive than lock/unlock (but this is not the scope of the question anymore).
– KabCode
Jan 28 at 10:28
@KabCode why would you set the value often? Each thread should minimize access to shared memory. When writing several times you should write only the last value, unless other threads are supposed to see the intermediate values, but then you have no choice other than locking for each write
– user463035818
Jan 28 at 11:09
@KabCode ah I think now I got what you wanted to say. You are worried by the overhead of creating a new lock instead of reusing the object (?). In that case I suggest you to take a look at the implementation, the lock isnt really doing much, it merely locks the mutex and realeases it, there is not much overhead
– user463035818
Jan 28 at 11:11
add a comment |
Using an extra scope specifically to limit the lifetime of an std::lock_guard object is indeed good practice. As the other answers point out, locking your mutex for the shortest period of time will reduce the chances that another thread will block on the mutex.
I see one more point that was not mentioned in the other answers: transactional operations. Let's use the classical example of a money transfer between two bank accounts. For your banking program to be correct, the modification of the two bank account's balance must be done without unlocking the mutex in between. Otherwise, it would be possible for another thread to lock the mutex while the program is in a weird state where only one of the accounts was credited/debited while the other account's balance was untouched!
With this in mind, it is not enough to ensure that the mutex is locked when each shared resource is modified. Sometimes, you must keep the mutex locked for a period of time spanning the modification of all the shared resources that form a transaction.
EDIT:
If for some reason keeping the mutex locked for the whole duration of the transaction is not acceptable, you can use the following algorithm:
1. Lock mutex, read input data, unlock mutex.
2. Perform all needed computations, save results locally.
3. Lock mutex, check that input data has not changed, perform the transaction with readily available results, unlock the mutex.
If the input data has changed during the execution of step 2, throw away the results and start over with the fresh input data.
Good point - could you provide a (pseudo)code sample how you would solve this issue programmatically?
– KabCode
Jan 29 at 8:29
add a comment |
I don't see the reason to do it.
If you do something so simple as "set one variable" - use atomic<> and you don't need mutex and lock at all. If you do something complicated - extract this code into new function and use lock in its first line.
But it is not always worth it to extract into a new function, right?
– L. F.
Jan 29 at 11:16
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54399719%2fincluding-stdlock-guard-in-extra-scope%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
Yes, it certainly makes sense to limit the scope of lock guards to be as short as possible, but not shorter.
The longer you hold a lock, the more likely it is that a thread will block waiting for that lock, which impacts performance as is thus usually considered a bad thing.
However, you must make sure that the program is still correct and that the lock is held at all times when it must be, i.e. when the shared resource protected by the lock is accessed or modified.
There may be one more point to consider (I do not have enough practical experience here to speak with certainty). Locking/releasing a mutex can potentially be an operation with nontrivial performance costs itself. Therefore, it may turn out that keeping a lock for a slightly longer period instead of unlocking & re-locking it several times in the course of one operation can actually improve overall performace. This is something which profiling could show you.
add a comment |
Yes, it certainly makes sense to limit the scope of lock guards to be as short as possible, but not shorter.
The longer you hold a lock, the more likely it is that a thread will block waiting for that lock, which impacts performance as is thus usually considered a bad thing.
However, you must make sure that the program is still correct and that the lock is held at all times when it must be, i.e. when the shared resource protected by the lock is accessed or modified.
There may be one more point to consider (I do not have enough practical experience here to speak with certainty). Locking/releasing a mutex can potentially be an operation with nontrivial performance costs itself. Therefore, it may turn out that keeping a lock for a slightly longer period instead of unlocking & re-locking it several times in the course of one operation can actually improve overall performace. This is something which profiling could show you.
add a comment |
Yes, it certainly makes sense to limit the scope of lock guards to be as short as possible, but not shorter.
The longer you hold a lock, the more likely it is that a thread will block waiting for that lock, which impacts performance as is thus usually considered a bad thing.
However, you must make sure that the program is still correct and that the lock is held at all times when it must be, i.e. when the shared resource protected by the lock is accessed or modified.
There may be one more point to consider (I do not have enough practical experience here to speak with certainty). Locking/releasing a mutex can potentially be an operation with nontrivial performance costs itself. Therefore, it may turn out that keeping a lock for a slightly longer period instead of unlocking & re-locking it several times in the course of one operation can actually improve overall performace. This is something which profiling could show you.
Yes, it certainly makes sense to limit the scope of lock guards to be as short as possible, but not shorter.
The longer you hold a lock, the more likely it is that a thread will block waiting for that lock, which impacts performance as is thus usually considered a bad thing.
However, you must make sure that the program is still correct and that the lock is held at all times when it must be, i.e. when the shared resource protected by the lock is accessed or modified.
There may be one more point to consider (I do not have enough practical experience here to speak with certainty). Locking/releasing a mutex can potentially be an operation with nontrivial performance costs itself. Therefore, it may turn out that keeping a lock for a slightly longer period instead of unlocking & re-locking it several times in the course of one operation can actually improve overall performace. This is something which profiling could show you.
answered Jan 28 at 10:17
AngewAngew
134k11258351
134k11258351
add a comment |
add a comment |
There might be a disadvantage: you cannot protect initializations this way. For example:
{
std::lock_guard<std::mutex> lock(mut);
Some_resource var{shared_var};
} // woops! var is lost
You have to use assignment like this:
Some_resource var;
{
std::lock_guard<std::mutex> lock(mut);
var = shared_Var;
}
Which can be not as good, you know, since for some types, dummy initialization (I intentionally avoided the term "default initialization") and then assignment is less efficient than directly initialization. (I intentionally avoided the term "direct initialization") Furthermore, in some situations, you cannot change the variable after initialization. (e.g. const
ones)
@P i pointed out this solution:
// use an immediately-invoked temporary lambda
Some_resource var {
[&] {
std::lock_guard<std::mutex> lock(mut);
return shared_var;
} () // parentheses for invoke
};
This way, with return value optimization, which is implemented well and properly by almost all popular modern compilers, and which is mandated as of C++17, you can do exactly what is expected.
1
Hi, what's wrong? I am willing to improve :)
– L. F.
Jan 28 at 10:28
3
You could use immediately invoked lambda
– user32434999
Jan 28 at 10:31
1
@KabCode It is as the scope is only due to the scheme mentioned in the question
– user32434999
Jan 28 at 10:33
2
Some_resource var{ [&]() { std::lock_guard<std::mutex> lock(mut); return shared_var; }() };
– user32434999
Jan 28 at 10:43
1
@L.F. I also think shared_var deserves a const
– user32434999
Jan 28 at 10:49
|
show 10 more comments
There might be a disadvantage: you cannot protect initializations this way. For example:
{
std::lock_guard<std::mutex> lock(mut);
Some_resource var{shared_var};
} // woops! var is lost
You have to use assignment like this:
Some_resource var;
{
std::lock_guard<std::mutex> lock(mut);
var = shared_Var;
}
Which can be not as good, you know, since for some types, dummy initialization (I intentionally avoided the term "default initialization") and then assignment is less efficient than directly initialization. (I intentionally avoided the term "direct initialization") Furthermore, in some situations, you cannot change the variable after initialization. (e.g. const
ones)
@P i pointed out this solution:
// use an immediately-invoked temporary lambda
Some_resource var {
[&] {
std::lock_guard<std::mutex> lock(mut);
return shared_var;
} () // parentheses for invoke
};
This way, with return value optimization, which is implemented well and properly by almost all popular modern compilers, and which is mandated as of C++17, you can do exactly what is expected.
1
Hi, what's wrong? I am willing to improve :)
– L. F.
Jan 28 at 10:28
3
You could use immediately invoked lambda
– user32434999
Jan 28 at 10:31
1
@KabCode It is as the scope is only due to the scheme mentioned in the question
– user32434999
Jan 28 at 10:33
2
Some_resource var{ [&]() { std::lock_guard<std::mutex> lock(mut); return shared_var; }() };
– user32434999
Jan 28 at 10:43
1
@L.F. I also think shared_var deserves a const
– user32434999
Jan 28 at 10:49
|
show 10 more comments
There might be a disadvantage: you cannot protect initializations this way. For example:
{
std::lock_guard<std::mutex> lock(mut);
Some_resource var{shared_var};
} // woops! var is lost
You have to use assignment like this:
Some_resource var;
{
std::lock_guard<std::mutex> lock(mut);
var = shared_Var;
}
Which can be not as good, you know, since for some types, dummy initialization (I intentionally avoided the term "default initialization") and then assignment is less efficient than directly initialization. (I intentionally avoided the term "direct initialization") Furthermore, in some situations, you cannot change the variable after initialization. (e.g. const
ones)
@P i pointed out this solution:
// use an immediately-invoked temporary lambda
Some_resource var {
[&] {
std::lock_guard<std::mutex> lock(mut);
return shared_var;
} () // parentheses for invoke
};
This way, with return value optimization, which is implemented well and properly by almost all popular modern compilers, and which is mandated as of C++17, you can do exactly what is expected.
There might be a disadvantage: you cannot protect initializations this way. For example:
{
std::lock_guard<std::mutex> lock(mut);
Some_resource var{shared_var};
} // woops! var is lost
You have to use assignment like this:
Some_resource var;
{
std::lock_guard<std::mutex> lock(mut);
var = shared_Var;
}
Which can be not as good, you know, since for some types, dummy initialization (I intentionally avoided the term "default initialization") and then assignment is less efficient than directly initialization. (I intentionally avoided the term "direct initialization") Furthermore, in some situations, you cannot change the variable after initialization. (e.g. const
ones)
@P i pointed out this solution:
// use an immediately-invoked temporary lambda
Some_resource var {
[&] {
std::lock_guard<std::mutex> lock(mut);
return shared_var;
} () // parentheses for invoke
};
This way, with return value optimization, which is implemented well and properly by almost all popular modern compilers, and which is mandated as of C++17, you can do exactly what is expected.
edited Jan 29 at 5:51
answered Jan 28 at 10:27
L. F.L. F.
1,194418
1,194418
1
Hi, what's wrong? I am willing to improve :)
– L. F.
Jan 28 at 10:28
3
You could use immediately invoked lambda
– user32434999
Jan 28 at 10:31
1
@KabCode It is as the scope is only due to the scheme mentioned in the question
– user32434999
Jan 28 at 10:33
2
Some_resource var{ [&]() { std::lock_guard<std::mutex> lock(mut); return shared_var; }() };
– user32434999
Jan 28 at 10:43
1
@L.F. I also think shared_var deserves a const
– user32434999
Jan 28 at 10:49
|
show 10 more comments
1
Hi, what's wrong? I am willing to improve :)
– L. F.
Jan 28 at 10:28
3
You could use immediately invoked lambda
– user32434999
Jan 28 at 10:31
1
@KabCode It is as the scope is only due to the scheme mentioned in the question
– user32434999
Jan 28 at 10:33
2
Some_resource var{ [&]() { std::lock_guard<std::mutex> lock(mut); return shared_var; }() };
– user32434999
Jan 28 at 10:43
1
@L.F. I also think shared_var deserves a const
– user32434999
Jan 28 at 10:49
1
1
Hi, what's wrong? I am willing to improve :)
– L. F.
Jan 28 at 10:28
Hi, what's wrong? I am willing to improve :)
– L. F.
Jan 28 at 10:28
3
3
You could use immediately invoked lambda
– user32434999
Jan 28 at 10:31
You could use immediately invoked lambda
– user32434999
Jan 28 at 10:31
1
1
@KabCode It is as the scope is only due to the scheme mentioned in the question
– user32434999
Jan 28 at 10:33
@KabCode It is as the scope is only due to the scheme mentioned in the question
– user32434999
Jan 28 at 10:33
2
2
Some_resource var{ [&]() { std::lock_guard<std::mutex> lock(mut); return shared_var; }() };
– user32434999
Jan 28 at 10:43
Some_resource var{ [&]() { std::lock_guard<std::mutex> lock(mut); return shared_var; }() };
– user32434999
Jan 28 at 10:43
1
1
@L.F. I also think shared_var deserves a const
– user32434999
Jan 28 at 10:49
@L.F. I also think shared_var deserves a const
– user32434999
Jan 28 at 10:49
|
show 10 more comments
Yes, it makes sense.
There are no other advantages, and there are no side-effects (it is a good way to write it).
An even better way, is to extract it into a private member function (if you have an operation that is synchronized this way, you might as well give the operation its own name):
{
// all used variables beside the lock_guard are created and initialized somewhere else
...// do something
set_var(new_value);
... // do some other stuff (that might take longer)
}
void your_class::set_value(int new_value)
{
std::lock_guard<std::mutex> lock(mut);
shared_var = new_value;
}
Good addition. This will help to keep the code clean. Caveat might be if you have to set this variable often then the costs of creating new locks might be more expensive than lock/unlock (but this is not the scope of the question anymore).
– KabCode
Jan 28 at 10:28
@KabCode why would you set the value often? Each thread should minimize access to shared memory. When writing several times you should write only the last value, unless other threads are supposed to see the intermediate values, but then you have no choice other than locking for each write
– user463035818
Jan 28 at 11:09
@KabCode ah I think now I got what you wanted to say. You are worried by the overhead of creating a new lock instead of reusing the object (?). In that case I suggest you to take a look at the implementation, the lock isnt really doing much, it merely locks the mutex and realeases it, there is not much overhead
– user463035818
Jan 28 at 11:11
add a comment |
Yes, it makes sense.
There are no other advantages, and there are no side-effects (it is a good way to write it).
An even better way, is to extract it into a private member function (if you have an operation that is synchronized this way, you might as well give the operation its own name):
{
// all used variables beside the lock_guard are created and initialized somewhere else
...// do something
set_var(new_value);
... // do some other stuff (that might take longer)
}
void your_class::set_value(int new_value)
{
std::lock_guard<std::mutex> lock(mut);
shared_var = new_value;
}
Good addition. This will help to keep the code clean. Caveat might be if you have to set this variable often then the costs of creating new locks might be more expensive than lock/unlock (but this is not the scope of the question anymore).
– KabCode
Jan 28 at 10:28
@KabCode why would you set the value often? Each thread should minimize access to shared memory. When writing several times you should write only the last value, unless other threads are supposed to see the intermediate values, but then you have no choice other than locking for each write
– user463035818
Jan 28 at 11:09
@KabCode ah I think now I got what you wanted to say. You are worried by the overhead of creating a new lock instead of reusing the object (?). In that case I suggest you to take a look at the implementation, the lock isnt really doing much, it merely locks the mutex and realeases it, there is not much overhead
– user463035818
Jan 28 at 11:11
add a comment |
Yes, it makes sense.
There are no other advantages, and there are no side-effects (it is a good way to write it).
An even better way, is to extract it into a private member function (if you have an operation that is synchronized this way, you might as well give the operation its own name):
{
// all used variables beside the lock_guard are created and initialized somewhere else
...// do something
set_var(new_value);
... // do some other stuff (that might take longer)
}
void your_class::set_value(int new_value)
{
std::lock_guard<std::mutex> lock(mut);
shared_var = new_value;
}
Yes, it makes sense.
There are no other advantages, and there are no side-effects (it is a good way to write it).
An even better way, is to extract it into a private member function (if you have an operation that is synchronized this way, you might as well give the operation its own name):
{
// all used variables beside the lock_guard are created and initialized somewhere else
...// do something
set_var(new_value);
... // do some other stuff (that might take longer)
}
void your_class::set_value(int new_value)
{
std::lock_guard<std::mutex> lock(mut);
shared_var = new_value;
}
answered Jan 28 at 10:19
utnapistimutnapistim
22.2k23572
22.2k23572
Good addition. This will help to keep the code clean. Caveat might be if you have to set this variable often then the costs of creating new locks might be more expensive than lock/unlock (but this is not the scope of the question anymore).
– KabCode
Jan 28 at 10:28
@KabCode why would you set the value often? Each thread should minimize access to shared memory. When writing several times you should write only the last value, unless other threads are supposed to see the intermediate values, but then you have no choice other than locking for each write
– user463035818
Jan 28 at 11:09
@KabCode ah I think now I got what you wanted to say. You are worried by the overhead of creating a new lock instead of reusing the object (?). In that case I suggest you to take a look at the implementation, the lock isnt really doing much, it merely locks the mutex and realeases it, there is not much overhead
– user463035818
Jan 28 at 11:11
add a comment |
Good addition. This will help to keep the code clean. Caveat might be if you have to set this variable often then the costs of creating new locks might be more expensive than lock/unlock (but this is not the scope of the question anymore).
– KabCode
Jan 28 at 10:28
@KabCode why would you set the value often? Each thread should minimize access to shared memory. When writing several times you should write only the last value, unless other threads are supposed to see the intermediate values, but then you have no choice other than locking for each write
– user463035818
Jan 28 at 11:09
@KabCode ah I think now I got what you wanted to say. You are worried by the overhead of creating a new lock instead of reusing the object (?). In that case I suggest you to take a look at the implementation, the lock isnt really doing much, it merely locks the mutex and realeases it, there is not much overhead
– user463035818
Jan 28 at 11:11
Good addition. This will help to keep the code clean. Caveat might be if you have to set this variable often then the costs of creating new locks might be more expensive than lock/unlock (but this is not the scope of the question anymore).
– KabCode
Jan 28 at 10:28
Good addition. This will help to keep the code clean. Caveat might be if you have to set this variable often then the costs of creating new locks might be more expensive than lock/unlock (but this is not the scope of the question anymore).
– KabCode
Jan 28 at 10:28
@KabCode why would you set the value often? Each thread should minimize access to shared memory. When writing several times you should write only the last value, unless other threads are supposed to see the intermediate values, but then you have no choice other than locking for each write
– user463035818
Jan 28 at 11:09
@KabCode why would you set the value often? Each thread should minimize access to shared memory. When writing several times you should write only the last value, unless other threads are supposed to see the intermediate values, but then you have no choice other than locking for each write
– user463035818
Jan 28 at 11:09
@KabCode ah I think now I got what you wanted to say. You are worried by the overhead of creating a new lock instead of reusing the object (?). In that case I suggest you to take a look at the implementation, the lock isnt really doing much, it merely locks the mutex and realeases it, there is not much overhead
– user463035818
Jan 28 at 11:11
@KabCode ah I think now I got what you wanted to say. You are worried by the overhead of creating a new lock instead of reusing the object (?). In that case I suggest you to take a look at the implementation, the lock isnt really doing much, it merely locks the mutex and realeases it, there is not much overhead
– user463035818
Jan 28 at 11:11
add a comment |
Using an extra scope specifically to limit the lifetime of an std::lock_guard object is indeed good practice. As the other answers point out, locking your mutex for the shortest period of time will reduce the chances that another thread will block on the mutex.
I see one more point that was not mentioned in the other answers: transactional operations. Let's use the classical example of a money transfer between two bank accounts. For your banking program to be correct, the modification of the two bank account's balance must be done without unlocking the mutex in between. Otherwise, it would be possible for another thread to lock the mutex while the program is in a weird state where only one of the accounts was credited/debited while the other account's balance was untouched!
With this in mind, it is not enough to ensure that the mutex is locked when each shared resource is modified. Sometimes, you must keep the mutex locked for a period of time spanning the modification of all the shared resources that form a transaction.
EDIT:
If for some reason keeping the mutex locked for the whole duration of the transaction is not acceptable, you can use the following algorithm:
1. Lock mutex, read input data, unlock mutex.
2. Perform all needed computations, save results locally.
3. Lock mutex, check that input data has not changed, perform the transaction with readily available results, unlock the mutex.
If the input data has changed during the execution of step 2, throw away the results and start over with the fresh input data.
Good point - could you provide a (pseudo)code sample how you would solve this issue programmatically?
– KabCode
Jan 29 at 8:29
add a comment |
Using an extra scope specifically to limit the lifetime of an std::lock_guard object is indeed good practice. As the other answers point out, locking your mutex for the shortest period of time will reduce the chances that another thread will block on the mutex.
I see one more point that was not mentioned in the other answers: transactional operations. Let's use the classical example of a money transfer between two bank accounts. For your banking program to be correct, the modification of the two bank account's balance must be done without unlocking the mutex in between. Otherwise, it would be possible for another thread to lock the mutex while the program is in a weird state where only one of the accounts was credited/debited while the other account's balance was untouched!
With this in mind, it is not enough to ensure that the mutex is locked when each shared resource is modified. Sometimes, you must keep the mutex locked for a period of time spanning the modification of all the shared resources that form a transaction.
EDIT:
If for some reason keeping the mutex locked for the whole duration of the transaction is not acceptable, you can use the following algorithm:
1. Lock mutex, read input data, unlock mutex.
2. Perform all needed computations, save results locally.
3. Lock mutex, check that input data has not changed, perform the transaction with readily available results, unlock the mutex.
If the input data has changed during the execution of step 2, throw away the results and start over with the fresh input data.
Good point - could you provide a (pseudo)code sample how you would solve this issue programmatically?
– KabCode
Jan 29 at 8:29
add a comment |
Using an extra scope specifically to limit the lifetime of an std::lock_guard object is indeed good practice. As the other answers point out, locking your mutex for the shortest period of time will reduce the chances that another thread will block on the mutex.
I see one more point that was not mentioned in the other answers: transactional operations. Let's use the classical example of a money transfer between two bank accounts. For your banking program to be correct, the modification of the two bank account's balance must be done without unlocking the mutex in between. Otherwise, it would be possible for another thread to lock the mutex while the program is in a weird state where only one of the accounts was credited/debited while the other account's balance was untouched!
With this in mind, it is not enough to ensure that the mutex is locked when each shared resource is modified. Sometimes, you must keep the mutex locked for a period of time spanning the modification of all the shared resources that form a transaction.
EDIT:
If for some reason keeping the mutex locked for the whole duration of the transaction is not acceptable, you can use the following algorithm:
1. Lock mutex, read input data, unlock mutex.
2. Perform all needed computations, save results locally.
3. Lock mutex, check that input data has not changed, perform the transaction with readily available results, unlock the mutex.
If the input data has changed during the execution of step 2, throw away the results and start over with the fresh input data.
Using an extra scope specifically to limit the lifetime of an std::lock_guard object is indeed good practice. As the other answers point out, locking your mutex for the shortest period of time will reduce the chances that another thread will block on the mutex.
I see one more point that was not mentioned in the other answers: transactional operations. Let's use the classical example of a money transfer between two bank accounts. For your banking program to be correct, the modification of the two bank account's balance must be done without unlocking the mutex in between. Otherwise, it would be possible for another thread to lock the mutex while the program is in a weird state where only one of the accounts was credited/debited while the other account's balance was untouched!
With this in mind, it is not enough to ensure that the mutex is locked when each shared resource is modified. Sometimes, you must keep the mutex locked for a period of time spanning the modification of all the shared resources that form a transaction.
EDIT:
If for some reason keeping the mutex locked for the whole duration of the transaction is not acceptable, you can use the following algorithm:
1. Lock mutex, read input data, unlock mutex.
2. Perform all needed computations, save results locally.
3. Lock mutex, check that input data has not changed, perform the transaction with readily available results, unlock the mutex.
If the input data has changed during the execution of step 2, throw away the results and start over with the fresh input data.
edited Mar 11 at 21:42
answered Jan 29 at 8:13
L.-C. CaronL.-C. Caron
1273
1273
Good point - could you provide a (pseudo)code sample how you would solve this issue programmatically?
– KabCode
Jan 29 at 8:29
add a comment |
Good point - could you provide a (pseudo)code sample how you would solve this issue programmatically?
– KabCode
Jan 29 at 8:29
Good point - could you provide a (pseudo)code sample how you would solve this issue programmatically?
– KabCode
Jan 29 at 8:29
Good point - could you provide a (pseudo)code sample how you would solve this issue programmatically?
– KabCode
Jan 29 at 8:29
add a comment |
I don't see the reason to do it.
If you do something so simple as "set one variable" - use atomic<> and you don't need mutex and lock at all. If you do something complicated - extract this code into new function and use lock in its first line.
But it is not always worth it to extract into a new function, right?
– L. F.
Jan 29 at 11:16
add a comment |
I don't see the reason to do it.
If you do something so simple as "set one variable" - use atomic<> and you don't need mutex and lock at all. If you do something complicated - extract this code into new function and use lock in its first line.
But it is not always worth it to extract into a new function, right?
– L. F.
Jan 29 at 11:16
add a comment |
I don't see the reason to do it.
If you do something so simple as "set one variable" - use atomic<> and you don't need mutex and lock at all. If you do something complicated - extract this code into new function and use lock in its first line.
I don't see the reason to do it.
If you do something so simple as "set one variable" - use atomic<> and you don't need mutex and lock at all. If you do something complicated - extract this code into new function and use lock in its first line.
answered Jan 29 at 8:39
EzhEzh
2651418
2651418
But it is not always worth it to extract into a new function, right?
– L. F.
Jan 29 at 11:16
add a comment |
But it is not always worth it to extract into a new function, right?
– L. F.
Jan 29 at 11:16
But it is not always worth it to extract into a new function, right?
– L. F.
Jan 29 at 11:16
But it is not always worth it to extract into a new function, right?
– L. F.
Jan 29 at 11:16
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54399719%2fincluding-stdlock-guard-in-extra-scope%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown