tutorial5.docu 5.04 KB
Newer Older
1
2
/** \page ex5 Using threads from within a plugin

3
4
When developing plugins for %OpenFlipper it is possible to start certain functions
in a new thread.
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Note that when using threading there are a few things the developer has to be aware of:

- Using threads can cause %OpenFlipper to crash since the threading
  interface is <b>still under development</b> at the moment.
- The developer should be aware of the fact that threads run necessarily independent
  from the main thread (handling all of Qt's user interface elements). So accessing
  any of these gui objects won't be possible from within a thread. This means
  in particular that logging, updating of views, etc. always has to be done via a queued
  signal/slot connection between the thread function and the plugin.
- The developer will have to pay special attention to avoid race conditions
  and dead-lock situations himself since %OpenFlipper can obviously not keep track of this.
  
\section OFT OpenFlipperThreads in theory and practice

Using threads in %OpenFlipper is quite simple. All necessary class definitions are already
available to plugins. So the first thing we have to do is instanciate an OpenFlipperThread object
and tell the core that we just launched a thread via startJob():

\code
OpenFlipperThread* thread = new OpenFlipperThread("My thread");
emit startJob( "My thread", "My thread's description" , 0 , 100 , false);
\endcode

The first parameter of the constructor of the OpenFlipperThread class denotes
the identifier of the thread. We will use this id to reference our thread later on.
The signal startJob() just tells the core that we are about to add a new thread
named "My thread" and having "My thread's description" as the description (OpenFlipper
32
displays this in the process manager). The third and fourth parameter determine the
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
progress scale (here going from 0 to 100). This should ideally correspond to the
number of steps the thread's process consists of with desireably equal computation time
per step. This is later used to inform the user about the progress of our process.
The last parameter indicates whether the process should be blocking or not. If it is declared as blocking,
all input to %OpenFlipper will be ignored until the processing has finished.
If a thread is declared as non-blocking, the thread will appear in %OpenFlipper's process manager
and the user will be able to normally use %OpenFlipper while the thread is running in the background.

The next thing we do is connecting the thread's signal to local signals/slots in order to
keep track of updates concering the processing of our thread:

\code
connect(thread, SIGNAL(finished(QString)),   this, SIGNAL(finishJob(QString)));

connect(thread, SIGNAL(function(QString)),   this, SLOT(testFunctionThread(QString)), Qt::DirectConnection);
\endcode

50
51
52
53
54
The thread emits the finished() signal when processing is done and the thread has been destroyed.
We need to make sure to at least connect this signal to the global finishJob() signal of the ProcessInterface so that the thread is removed from the process manager window.
Additionally, if we want to do additional steps after the processing has finished (e.g. call updatedObject() so that our change to an object are made visible), we can connect finished() to a function that takes care of that.

The function() signal needs to be connected to the function that will actually be processed in the thread.
55
56
57
58
59
60
61
62
63
64
65
Once having connected these signals, we're about to start our thread:

\code
// Launch the thread
thread->start();

// Start processing of the function code (connected via signal OpenFlipperThread::function())
thread->startProcessing();
\endcode

Now our function is processed within a new thread and either %OpenFlipper's process manager or
66
a blocking widget showing the thread's progress pops up (depending on whether the thread is declared
67
to be blocking or not). So the only thing that is still left to take core of is the continuous update
68
of the thread's progress state. Assuming we run the following function in a separate thread:
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

\code
void testFunctionThread(QString _jobId) {

    for (int i = 0 ; i < 1000000; ++i) {
       
        // Emit new progress state
        if(i % 10000 == 0)
            emit setJobState(_jobId, (int)(i / 10000));
        
        if(i == 500000) {
            // When half of the processing is finished
            // change the job's description
            emit setJobDescription(_jobId, "Half way done");
        }
    }
}
\endcode

88
89
This function contains a loop incrementing variable i one million times.
Each 10000-th iteration we emit the signal setJobState(QString,int)
90
91
where we set the second parameter to an integer value between 0 and 100 (remember that we set
these limits at the beginning when emitting the startJob() signal).
92
%OpenFlipper now updates its progress bar state to the new value (scaled to a percentage of the interval that was defined in startJob).
93
94
95
If half of the loop is processed (i == 500000), we additionally want to update
the progresses' description that is also displayed in %OpenFlipper's progress manager or the
blocking widget of the thread, respectively.
96
97

*/