PDA

View Full Version : Launching a process from a thread.



alexcodercpp
6th August 2015, 06:18
I have to permorm a series of hard disk operations in the background, so the main gui should not freeze. All these operations I'm going to launch using QProcess, adding arguments to it as bash commands.
Quite important thing to note is that a user has 2 options while these operations are going on in the background: 1) to quit the application 2) to stop the series of operations after the current operations is finished.

What is the best strategy to build my app : 1) to create a thread and then create processes in it or 2) create processes without creating threads?

jefftee
6th August 2015, 07:14
What benefit does option #1 provide that option #2 doesn't? i.e. If the real work is done via a new process, then there is no benefit IMHO to create a separate thread in the first place, unless there's some other reason you didn't state above.

My recommendation is to use the simplest design that meets your requirements. Threads are overused/misused by lots of people and serve no purpose other than to create hard to find bugs when not implemented properly.

Hope that helps.

alexcodercpp
6th August 2015, 18:28
I'm going to describe a little bit more in detail my situation.

1)I have a container of widgets std::vector<QWidget*>container.
2)Each widget has a method, let's call it launch_process(). It launches a process correspoindant to it's widget.
3)The user presses start button and we have a loop: for(auto i : container) i->launch_process();
4) I also must provide the user with posibility to stop the series of processes (the loop) after the current process is finished.
5)So my idea was to write the loop this way : for(std::size_t i = 0; i < container.size() && !stop_pressed();++i) i->launch_process();
6)When the user presses the stop button, he changes a private member stopped_;
7) After the next launched process is terminated, stop_pressed() is called to check the state the private method stopped_ - if it's true - we're out of the loop.

I've been choosing what to put inside launch_process() - 1) a thread and only then a process in it or 2) directly a process without threads.
The main goal is to change stopped_ member while thread or process is "out there".

yeye_olive
6th August 2015, 18:58
What does launch_process() do?

From your description, it seems to start another process, then quickly return, leaving the process running in the background. No synchronization with the external process is done at this point. But then, you will never be fast enough to stop the loop. It will quickly start all the processes, which will all run concurrently with your program.

I suggest you first explain what is supposed to run, when it is supposed to run (and what is supposed to run concurrently with what) before we help you work out a solution based on external processes, threads, high-level concurrent APIs, etc.

anda_skoa
6th August 2015, 19:17
That sounds more like a job queue.

- Check if you should stop, if yes, clear queue, done
- Take the first job out of the queue
- Connect job "finished" signal to some slot
- Run that job
- When the job signals it is done, continue at the beginning

Cheers,
_

alexcodercpp
6th August 2015, 19:25
To yeye_olive

1) Inside each launch_process() will be launched simple bash command (I'm using linux) to perform disk operations.
2) These launches should be consequential, in a loop, the next launches only and only when the previous is terminated.
3) Each process does different job - one works with usb, another one with cd , third with hard disk and so on.
4) Each process in the meantime should provide some feedback to the main GUI thread - like progress bar etc.

jefftee
6th August 2015, 23:39
I'm going to describe a little bit more in detail my situation.

1)I have a container of widgets std::vector<QWidget*>container.
2)Each widget has a method, let's call it launch_process(). It launches a process correspoindant to it's widget.
3)The user presses start button and we have a loop: for(auto i : container) i->launch_process();
4) I also must provide the user with posibility to stop the series of processes (the loop) after the current process is finished.
5)So my idea was to write the loop this way : for(std::size_t i = 0; i < container.size() && !stop_pressed();++i) i->launch_process();
6)When the user presses the stop button, he changes a private member stopped_;
7) After the next launched process is terminated, stop_pressed() is called to check the state the private method stopped_ - if it's true - we're out of the loop.

I've been choosing what to put inside launch_process() - 1) a thread and only then a process in it or 2) directly a process without threads.
The main goal is to change stopped_ member while thread or process is "out there".
I assume your processes will be writing output to stdout or stderr that indicate their progress, which you can readily consume by hooking up the QProcess::readyReadStandardError() or QProcess:readyReadStandardOutput(). That said, I believe you will need to use QProcess::startDetached() due to your requirement that the user can exit your app while the processes are still running. In that case, your app would need to start the processes and redirect their stdout/stderr to files, which can then be opened and read by your application, etc.

As far as stopping the running processes, you can use QProcess::terminate() but I would caution you about killing running processes if they are modifying data or changing disk metadata, etc. You may also find instances where you need to use a bigger hammer to terminate a running QProcess, requiring an OS specific technique.

Your GUI will be unresponsive while you loop through your container starting QProcess's. You didn't indicate how many Widgets in your container, if a small number (subjective), then I don't believe a separate thread would be of any benefit. If you have a large (subjective) number of widgets to loop through, then you may find it desirable to use a thread and then emit a signal in your GUI thread that sends the info to your thread and start the QProcess, etc. You could also add a call to QApplication::processEvents() to your loop that should keep your GUI responsive.

If you do decide to launch the QProcesses using a separate thread, I would recommend that you use QtConcurrent::run() to run your launch_process() in a separate thread as opposed to sub-classing QThread or the other "Move To Thread" approach (Google it).

Hope that helps.

anda_skoa
7th August 2015, 09:56
That said, I believe you will need to use QProcess::startDetached() due to your requirement that the user can exit your app while the processes are still running.

Instead of losing control over the process, I would recommend to keep the application running instead, but just close all its windows.

So there won't be any difference between normal operation and "user closes app but wants all jobs to continue".



Your GUI will be unresponsive while you loop through your container starting QProcess's

If I understood correctly, there won't be any explicit loop.
The job sequence only runs one job after the previous one has finished, so there is always only one job running any given time.

Cheers,
_

jefftee
7th August 2015, 18:15
Instead of losing control over the process, I would recommend to keep the application running instead, but just close all its windows.

So there won't be any difference between normal operation and "user closes app but wants all jobs to continue".
_
I would also agree that would be my approach, however, since the app could crash or be closed for some other reason, I think the OP should still design some type of persistent process status into his app so that his app can keep track of the tasks status whether or not his app remains running.


If I understood correctly, there won't be any explicit loop.
The job sequence only runs one job after the previous one has finished, so there is always only one job running any given time.
_
Agreed, I only suggested the potential need for this based on the for loop he described in #5 of the OP's detailed description. I have no idea what the will be done in the for loop, so it may not require any special consideration.

Thanks