279
61
auto proot = pop.root();
62
63
cout << "Counter = " << proot->counter << endl;
64
65
std::vector workers;
66
workers.reserve(10);
67
for (int i = 0; i < 10; ++i) {
68
workers.emplace_back(increment, std::ref(pop));
69
}
70
71
for (int i = 0; i < 10; ++i) {
72
workers[i].join();
73
}
74
75
cout << "Counter = " << proot->counter << endl;
76
77
pop.close();
78
return 0;
79 }
You might expect that the program in Listing 14-1 the prints a final counter value
of 10. However, PMDK transactions do not automatically support isolation from the
ACID properties set. The result of the increment operation on line 53 is visible to
other concurrent transactions before the current transaction has implicitly committed
its update on line 54. That is, a simple data race is occurring in this example. A race
condition occurs when two or more threads can access shared data and they try to
change it at the same time. Because the operating system’s thread scheduling algorithm
can swap between threads at any time, there is no way for the application to know the
order in which the threads will attempt to access the shared data. Therefore, the result
of the change of the data is dependent on the thread scheduling algorithm, that is, both
threads are “racing” to access/change the data.
If we run this example multiple times, the results will vary from run to run. We can
try to fix the race condition by acquiring a mutex lock before the counter increment as
shown in Listing 14-2.
Chapter 14 ConCurrenCy and persistent MeMory
61
auto proot = pop.root();
62
63
cout << "Counter = " << proot->counter << endl;
64
65
std::vector
66
workers.reserve(10);
67
for (int i = 0; i < 10; ++i) {
68
workers.emplace_back(increment, std::ref(pop));
69
}
70
71
for (int i = 0; i < 10; ++i) {
72
workers[i].join();
73
}
74
75
cout << "Counter = " << proot->counter << endl;
76
77
pop.close();
78
return 0;
79 }
You might expect that the program in Listing 14-1 the prints a final counter value
of 10. However, PMDK transactions do not automatically support isolation from the
ACID properties set. The result of the increment operation on line 53 is visible to
other concurrent transactions before the current transaction has implicitly committed
its update on line 54. That is, a simple data race is occurring in this example. A race
condition occurs when two or more threads can access shared data and they try to
change it at the same time. Because the operating system’s thread scheduling algorithm
can swap between threads at any time, there is no way for the application to know the
order in which the threads will attempt to access the shared data. Therefore, the result
of the change of the data is dependent on the thread scheduling algorithm, that is, both
threads are “racing” to access/change the data.
If we run this example multiple times, the results will vary from run to run. We can
try to fix the race condition by acquiring a mutex lock before the counter increment as
shown in Listing 14-2.
Chapter 14 ConCurrenCy and persistent MeMory
