.tutorial

git-svn-id: svn://ultimatepp.org/upp/trunk@10557 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2016-12-19 14:32:40 +00:00
parent 5908dd4c74
commit f12b0dc887
9 changed files with 248 additions and 5 deletions

View 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);
///
}

View 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);
///
}

View 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.
}

View file

@ -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;

View 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);
///
}

View file

@ -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

View file

@ -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).
}

View 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.

View file

@ -41,6 +41,10 @@ GUI_APP_MAIN
DO(ValueArrayMap);
DO(ThreadTutorial);
DO(MutexTutorial);
DO(ConditionVariableTutorial);
DO(CoWorkTutorial);
DO(CoPartitionTutorial);
MakeTutorial();
}