mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 06:05:58 -06:00
.tutorial
git-svn-id: svn://ultimatepp.org/upp/trunk@10557 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
5908dd4c74
commit
f12b0dc887
9 changed files with 248 additions and 5 deletions
56
tutorial/CoreTutorial/CoPartition.cpp
Normal file
56
tutorial/CoreTutorial/CoPartition.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#include "Tutorial.h"
|
||||
|
||||
void CoPartitionTutorial()
|
||||
{
|
||||
/// .CoPartition
|
||||
|
||||
Vector<int> data;
|
||||
for(int i = 0; i < 10000; i++)
|
||||
data.Add(i);
|
||||
|
||||
int sum = 0;
|
||||
|
||||
CoWork co;
|
||||
for(int i = 0; i < data.GetCount(); i++)
|
||||
co & [i, &sum, &data] { CoWork::FinLock(); sum += data[i]; };
|
||||
co.Finish();
|
||||
DUMP(sum);
|
||||
|
||||
///
|
||||
|
||||
sum = 0;
|
||||
CoPartition(data, [&sum](const auto& subrange) {
|
||||
int partial_sum = 0;
|
||||
for(const auto& x : subrange)
|
||||
partial_sum += x;
|
||||
CoWork::FinLock(); // available as CoPartition uses CoWork
|
||||
sum += partial_sum;
|
||||
});
|
||||
DUMP(sum);
|
||||
|
||||
///
|
||||
|
||||
sum = 0;
|
||||
CoPartition(data.begin(), data.end(), [&sum] (auto l, auto h) {
|
||||
int partial_sum = 0;
|
||||
while(l != h)
|
||||
partial_sum += *l++;
|
||||
CoWork::FinLock(); // available as CoPartition uses CoWork
|
||||
sum += partial_sum;
|
||||
});
|
||||
DUMP(sum);
|
||||
|
||||
///
|
||||
|
||||
sum = 0;
|
||||
CoPartition(0, data.GetCount(), [&sum, &data] (int l, int h) {
|
||||
int partial_sum = 0;
|
||||
while(l != h)
|
||||
partial_sum += data[l++];
|
||||
CoWork::FinLock(); // available as CoPartition uses CoWork
|
||||
sum += partial_sum;
|
||||
});
|
||||
DUMP(sum);
|
||||
|
||||
///
|
||||
}
|
||||
43
tutorial/CoreTutorial/CoWork.cpp
Normal file
43
tutorial/CoreTutorial/CoWork.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#include "Tutorial.h"
|
||||
|
||||
void CoWorkTutorial()
|
||||
{
|
||||
/// .`CoWork`
|
||||
|
||||
FileIn in(GetDataFile("test.txt")); // let us open some tutorial testing data
|
||||
|
||||
Index<String> w;
|
||||
Mutex m; // need mutex to serialize access to w
|
||||
|
||||
CoWork co;
|
||||
while(!in.IsEof()) {
|
||||
String ln = in.GetLine();
|
||||
co & [ln, &w, &m] {
|
||||
for(const auto& s : Split(ln, [](int c) { return IsAlpha(c) ? 0 : c; })) {
|
||||
Mutex::Lock __(m);
|
||||
w.FindAdd(s);
|
||||
}
|
||||
};
|
||||
}
|
||||
co.Finish();
|
||||
|
||||
DUMP(w);
|
||||
|
||||
///
|
||||
|
||||
|
||||
in.Seek(0);
|
||||
while(!in.IsEof()) {
|
||||
String ln = in.GetLine();
|
||||
co & [ln, &w, &m] {
|
||||
Vector<String> h = Split(ln, [](int c) { return IsAlpha(c) ? 0 : c; });
|
||||
CoWork::FinLock(); // replaces the mutex, locked till the end of CoWork job
|
||||
for(const auto& s : h)
|
||||
w.FindAdd(s);
|
||||
};
|
||||
}
|
||||
|
||||
DUMP(w);
|
||||
|
||||
///
|
||||
}
|
||||
50
tutorial/CoreTutorial/ConditionVariable.cpp
Normal file
50
tutorial/CoreTutorial/ConditionVariable.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#include "Tutorial.h"
|
||||
|
||||
void ConditionVariableTutorial()
|
||||
{
|
||||
/// .`ConditionVariable`
|
||||
|
||||
/// `ConditionVariable` in general is a synchronization primitive used to block/awaken the
|
||||
/// thread. `ConditionVariable` is associated with `Mutex` used to protect some data; in
|
||||
/// the thread that is to be blocked, `Mutex` has to locked; call to `Wait` atomically
|
||||
/// unlocks the `Mutex` and puts the thread to waiting. Another thread then can resume the
|
||||
/// thread by calling `Signal`, which also causes `Mutex` to lock again. Multiple threads
|
||||
/// can be waiting on single `ConditionVariable`; `Signal` resumes single waiting thread,
|
||||
/// `Brodcast` resumes all waitng threads.
|
||||
|
||||
bool stop = false;
|
||||
BiVector<int> data;
|
||||
Mutex m;
|
||||
ConditionVariable cv;
|
||||
|
||||
Thread t;
|
||||
t.Run([&stop, &data, &m, &cv] {
|
||||
Mutex::Lock __(m);
|
||||
for(;;) {
|
||||
while(data.GetCount()) {
|
||||
int q = data.PopTail();
|
||||
LOG("Data received: " << q);
|
||||
}
|
||||
if(stop)
|
||||
break;
|
||||
cv.Wait(m);
|
||||
}
|
||||
});
|
||||
|
||||
for(int i = 0; i < 10; i++) {
|
||||
{
|
||||
Mutex::Lock __(m);
|
||||
data.AddHead(i);
|
||||
}
|
||||
cv.Signal();
|
||||
Sleep(1);
|
||||
}
|
||||
stop = true;
|
||||
cv.Signal();
|
||||
t.Wait();
|
||||
|
||||
/// Important note: rarely thread can be resumed from `Wait` even if no other called
|
||||
/// `Signal`. This is not a bud, but ^https://en.wikipedia.org/wiki/Spurious_wakeup:design
|
||||
/// decision for performance reason^. In practice it only means that situation has to be
|
||||
/// (re)checked after resume.
|
||||
}
|
||||
|
|
@ -41,6 +41,11 @@ file
|
|||
Function.cpp,
|
||||
CapturingContainers.cpp,
|
||||
Thread.cpp,
|
||||
Mutex.cpp,
|
||||
ConditionVariable.cpp,
|
||||
test.txt,
|
||||
CoWork.cpp,
|
||||
CoPartition.cpp,
|
||||
tutorial2.cpp,
|
||||
help.qtf;
|
||||
|
||||
|
|
|
|||
51
tutorial/CoreTutorial/Mutex.cpp
Normal file
51
tutorial/CoreTutorial/Mutex.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#include "Tutorial.h"
|
||||
|
||||
void MutexTutorial()
|
||||
{
|
||||
/// .`Mutex`
|
||||
|
||||
/// Mutex ("mutual exclusion") is a well known concept in multithreaded programming: When
|
||||
/// multiple threads write and read the same data, the access has to be serialized using
|
||||
/// Mutex. Following invalid code demonstrates why:
|
||||
|
||||
Thread t;
|
||||
|
||||
int sum = 0;
|
||||
t.Run([&sum] {
|
||||
for(int i = 0; i < 1000000; i++)
|
||||
sum++;
|
||||
});
|
||||
|
||||
for(int i = 0; i < 1000000; i++)
|
||||
sum++;
|
||||
|
||||
t.Wait();
|
||||
DUMP(sum);
|
||||
|
||||
/// While the expected value is 2000000, produced value is different. The problem is that
|
||||
/// both thread read / modify / write `sum` value without any locking. Using `Mutex` locks
|
||||
/// the `sum` and thus serializes access to it - read / modify / write sequence is now
|
||||
/// exclusive for the thread that has `Mutex` locked, this fixing the issue. `Mutex` can be
|
||||
/// locked / unlocked with `Enter` / `Leave` methods. Alternatively, `Mutex::Lock` helper
|
||||
/// class locks `Mutex` in constructor and unlocks it in destructor:
|
||||
|
||||
Mutex m;
|
||||
sum = 0;
|
||||
t.Run([&sum, &m] {
|
||||
for(int i = 0; i < 1000000; i++) {
|
||||
m.Enter();
|
||||
sum++;
|
||||
m.Leave();
|
||||
}
|
||||
});
|
||||
|
||||
for(int i = 0; i < 1000000; i++) {
|
||||
Mutex::Lock __(m); // Lock m till the end of scope
|
||||
sum++;
|
||||
}
|
||||
|
||||
t.Wait();
|
||||
DUMP(sum);
|
||||
|
||||
///
|
||||
}
|
||||
|
|
@ -20,11 +20,11 @@ void NullTutorial()
|
|||
DUMP(d);
|
||||
DUMP(e > d);
|
||||
|
||||
/// Null is the only instance of `Nuller` type. Assigning `Null` to
|
||||
/// `Null` is the only instance of `Nuller` type. Assigning `Null` to
|
||||
/// primitive types is achieved by cast operators of `Nuller`, other types can do it using
|
||||
/// constructor from `Nuller`.
|
||||
|
||||
/// As a special case, if Value contains Null, it is convertible to any value type that can contain Null:
|
||||
/// As a special case, if `Value` contains `Null`, it is convertible to any value type that can contain `Null`:
|
||||
|
||||
Value v = x; // x is int
|
||||
e = v; // e is Date, but v is Null, so Null is assigned to e
|
||||
|
|
|
|||
|
|
@ -2,7 +2,21 @@
|
|||
|
||||
void ThreadTutorial()
|
||||
{
|
||||
/// .Thread
|
||||
/// .`Thread`
|
||||
|
||||
/// Since C++11, there is now a reasonable support for threads in standard library.
|
||||
/// There are however reasons to use U++ threads instead. One of them is that U++ high
|
||||
/// performance memory allocator needs a cleanup call at the the thread exit, which is
|
||||
/// naturally implemented into `Upp::Thread`. Second 'hard' reason is that Microsoft
|
||||
/// compiler is using Win32 API function for condition variable that are not available for
|
||||
/// Windows XP, while U++ has alternative implementation for Windows XP, thus making
|
||||
/// executable compatible with it.
|
||||
|
||||
/// Then of course we believe U++ multithreading / parallel programming support is easier
|
||||
/// to use and leads to higher performance...
|
||||
|
||||
/// `Thread` class can start the thread and allows launching thread to `Wait` for its
|
||||
/// completion:
|
||||
|
||||
Thread t;
|
||||
t.Run([] {
|
||||
|
|
@ -20,5 +34,19 @@ void ThreadTutorial()
|
|||
t.Wait();
|
||||
LOG("Wait for thread done");
|
||||
|
||||
///
|
||||
}
|
||||
/// `Thread` destructor calls `Detach` method with 'disconnects' `Thread` from the thread.
|
||||
/// Thread continues running.
|
||||
|
||||
/// `Thread::Start` static method launches a thread without possibility to wait for its
|
||||
/// completion; if you need to wait, you have to use some other method:
|
||||
|
||||
bool x = false;
|
||||
|
||||
Thread::Start([&x] { LOG("In the Started thread"); x = true; });
|
||||
|
||||
LOG("About to wait for thread to finish");
|
||||
while(!x) { Sleep(1); } // Do not do this in real code!
|
||||
LOG("Wait for thread done");
|
||||
|
||||
/// (method used here is horrible, but should demonstrate the point).
|
||||
}
|
||||
|
|
|
|||
6
tutorial/CoreTutorial/test.txt
Normal file
6
tutorial/CoreTutorial/test.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
||||
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
||||
consequat. Duis aute irure dolor in reprehenderit in voluptate velit
|
||||
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
|
||||
non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
|
|
@ -41,6 +41,10 @@ GUI_APP_MAIN
|
|||
DO(ValueArrayMap);
|
||||
|
||||
DO(ThreadTutorial);
|
||||
DO(MutexTutorial);
|
||||
DO(ConditionVariableTutorial);
|
||||
DO(CoWorkTutorial);
|
||||
DO(CoPartitionTutorial);
|
||||
|
||||
MakeTutorial();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue