In the following example when the "Submit" button is clicked the value of the static variable Count is incremented. But is this operation thread safe? Is using Appliation object the proper way of doing such operation? The questions are applicable to Web form application s as well.
The count always seem to increase as I click the Submit button.
View(Razor):
@{
Layout = null;
}
<html>
<body>
<form>
<p>@ViewBag.BeforeCount</p>
<input type="submit" value="Submit" />
</form>
</body>
</html>
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.BeforeCount = StaticVariableTester.Count;
StaticVariableTester.Count += 50;
return View();
}
}
Static Class:
public class StaticVariableTester
{
public static int Count;
}
No, it's not. The += operator is done in 3 steps: read the value of the variable, increase it by one, assign the new value. Expanded:
var count = StaticVariableTester.Count;
count = count + 50;
StaticVariableTester.Count = count;
A thread could be preempted between any two of these steps. This means that if Count
is 0, and two threads execute += 50
concurrently, it's possible Count
will be 50 instead of 100.
T1
reads Count
as 0.T2
reads Count
as 0T1
adds 0 + 50T2
adds 0 + 50T1
assigns 50 to Count
T2
assigns 50 to Count
Count
equals 50Additionally, it could also be preempted between your first two instructions. Which means two concurrent threads might both set ViewBag.BeforeCount
to 0, and only then increment StaticVariableTester.Count
.
Use a lock
private readonly object _countLock = new object();
public ActionResult Index()
{
lock(_countLock)
{
ViewBag.BeforeCount = StaticVariableTester.Count;
StaticVariableTester.Count += 50;
}
return View();
}
Or use Interlocked.Add
public static class StaticVariableTester
{
private static int _count;
public static int Count
{
get { return _count; }
}
public static int IncrementCount(int value)
{
//increments and returns the old value of _count
return Interlocked.Add(ref _count, value) - value;
}
}
public ActionResult Index()
{
ViewBag.BeforeCount = StaticVariableTester.IncrementCount(50);
return View();
}
Increment is not atomic so is not thread safe.
Check out Interlocked.Add
:
Adds two 32-bit integers and replaces the first integer with the sum, as an atomic operation.
You'd use it like this:
Interlocked.Add(ref StaticVariableTester.Count, 50);
Personally I'd wrap this in your StaticVariableTester
class:
public class StaticVariableTester
{
private static int count;
public static void Add(int i)
{
Interlocked.Add(ref count, i);
}
public static int Count
{
get { return count; }
}
}
If you want the returned values (as per dcastro's comment) then you could always do:
public static int AddAndGetNew(int i)
{
return Interlocked.Add(ref count, i);
}
public static int AddAndGetOld(int i)
{
return Interlocked.Add(ref count, i) - i;
}
In your code you could do
ViewBag.BeforeCount = StaticVariableTester.AddAndGetOld(50);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With