Warning, /frameworks/threadweaver/examples/HelloWorldRaw.in.md is written in an unsupported language. File is not indexed.
0001 ## Hello World! with queueing multiple jobs 0002 0003 The first example showed nothing that would have required multiple 0004 threads to print _Hello World!_, and also did not mention anything 0005 about at what time jobs get deleted. Object life span is of course a 0006 crucial questions when programming in C++. So of what type is the 0007 value that is returned by `make_job` in the first example? 0008 0009 The returned object is of type `JobPointer`, which is a 0010 `QSharedPointer` to `Job`. When `make_job` is executed, it allocates a 0011 `Job` that will later execute the C++ lambda function, and then embeds 0012 it into a shared pointer. Shared pointers count references to the 0013 object pointer they represent, and delete the object when the 0014 last reference to it is destroyed. In a way, they are single-object 0015 garbage collectors. In the example, the new job is immediately handed 0016 over to the queue stream, and no reference to it is kept by 0017 `main()`. This approach is often called "fire-and-forget jobs". 0018 The queue will process the job and forget about it when it 0019 has been completed. It will then definitely get deleted automatically, 0020 even though the programmer does not necessarily know exactly when. It 0021 could happen (and in the case of ThreadWeaver jobs commonly does) 0022 deeply in the bowels of Qt event handling when the last event holding 0023 a reference to the job gets destroyed. The gist of it is that from the 0024 programmers point of view, it is not necessary to keep a reference to 0025 a job and delete it later. With that in mind, no further memory 0026 management is required in the HelloWorld example, and the program is 0027 complete. 0028 0029 Fire-and-forget jobs are not always the right tool. For example, if 0030 a job is retrieving and parsing some data, the application needs to 0031 access the data once the job is complete. For that, the programmer 0032 could implement a custom job class. 0033 0034 @@snippet(HelloWorldRaw/HelloWorldRaw.cpp,sample-helloworldraw-class,cpp) 0035 0036 The `QDebugJob` class simply prints a message to `qDebug()` when it is 0037 executed. To implement such a custom job class, it is inherited from 0038 `ThreadWeaver::Job`. By overloading the `run()` method, the "payload", 0039 the operation performed by the job, is being defined. The parameters 0040 to the run method are the job as the queue sees it, and the thread 0041 that is executing the job. The first parameter may be surprising. The 0042 reason that there may be a difference between the job that the queue 0043 sees and `this` is that jobs may be decorated, that means wrapped in 0044 something else that waddles and quacks like a job, before being 0045 queued. How this works will be explained later, what is important to 0046 keep in mind for now is not to assume to always find `this` in the 0047 queue. 0048 0049 @@snippet(HelloWorldRaw/HelloWorldRaw.cpp,sample-helloworldraw-main,cpp) 0050 0051 This time, in the `main()` function, four jobs in total will be 0052 allocated. Two of them as local variables (j1 and j2), one (j3) 0053 dynamically and saved in a `JobPointer`, and finally j4 is allocated 0054 on the heap with `new`. 0055 All of them are then queued up for execution in one single 0056 command. Wait, what? Right. Local variables, job pointers and raw 0057 pointers are queued the same way and may be mixed and matched using 0058 the stream operators. When a local variable is queued, a special 0059 shared pointer will be used to hold it which does not delete the 0060 object when the reference count reaches zero. A `JobPointer` is simply 0061 a shared pointer. A raw pointer will be considered a new object and 0062 automatically wrapped in a shared pointer and deleted when it goes out 0063 of scope. Even though three different kinds of objects are handed over 0064 to the stream, in all three cases the programmer does not need to put 0065 special consideration into memory management and the object life 0066 cycles. 0067 0068 Now before executing the program, pause for a minute and think about 0069 what you expect it to print. 0070 0071 ~~~~ 0072 World! 0073 This is... 0074 Hello 0075 ThreadWeaver! 0076 ~~~~ 0077 0078 Four jobs are being queued all at the same time, that is when the 0079 `stream()` statement closes. Assuming there is more than one worker 0080 thread, the order of execution of the jobs is undefined. The strings 0081 will be printed in arbitrary order. In case this comes as a surprise, 0082 it is important to keep in mind that by default, there is no relation 0083 between jobs that defines their execution order. This behaviour is in 0084 line with how thread pools normally work. In ThreadWeaver, there are 0085 ways to influence the order of execution by declaring dependencies 0086 between them or aggregating multiple jobs into collections or 0087 sequences. More on that later. 0088 0089 Before the end of `main()`, the application will block and wait for 0090 the queue to finish all jobs. This was not needed in the first 0091 HelloWorld example, so why is it necessary here? As explained there, 0092 the global queue will be destroyed when the `QCoreApplication` object 0093 is destroyed. If `main()` would exit before j1 and j2 have been 0094 executed, it's local variables including j1 and j2 would be 0095 destroyed. In the destructor of `QCoreApplication` the queue would 0096 wait to finish all jobs, and try to execute j1 and j2, which have 0097 already been destructed. Mayhem would ensue. When using local 0098 variables as jobs, make sure that they have been completed before 0099 destroying them. The `finish()` method of the queue guarantees that it 0100 no more holds references to any jobs that have been executed. 0101