I want to limit the number of thread for a given job.
Here is a sample code of my attempts to do so:
#DEFINE MAX_THREAD_COUNT 5
Later:
void MyClass::a_method() {
unsigned thread_count = 0;
std::vector<std::thread> jobs;
std::vector<AnObject> collection;
for(auto& c : collection) {
if(thread_count >= MAX_THREAD_COUNT) {
for(auto& j : jobs) {
if(j.joinable()) {
j.join();
thread_count--;
}
}
}
thread_count++;
jobs.push_back(std::thread([=] { this->a_long_job(c); }));
}
for(auto& j : jobs) {
if(j.joinable()) {
j.join();
}
}
}
The problem is that sometimes this->a_long_job isn't call all the time, I checked this by printing a line inside the method and by printing the number of elements contained in collection.
Another idea that could work but isn't elegant would be to launch max_thread threads with c/max_thread calls of this->a_long_job(c) in each.
I don't think that I need a thread pool for this application because there's a need only in this method.
Notes:
No external libraries
C++11 and further
Related
I want to call qmodbusreply related monitoring function in the std thread.
void DigitalInputController::monitoring()
{
QModbusReply* reply = nullptr;
QModbusDataUnit d(QModbusDataUnit::Coils, 0, 8);
reply = m_pDevice->sendReadRequest(d, m_DeviceID);
if (reply)
{
if (!reply->isFinished())
{
connect(reply, &QModbusReply::finished, this, &DigitalInputController::receivedDigitalInputData);
}
else
{
delete reply;
}
}
}
The mainwindow source is as below.
:
:
std::thread th(&MainWindow::serialCommandQueueMonitoringThread, this);
th.detach();
for (int i=0; i<4; i++)
{
m_pPickingController[i]->m_pDigitalInputController->moveToThread(this->thread());
m_pPickingController[i]->moveToThread(this->thread());
}
:
:
void MainWindow::serialCommandQueueMonitoringThread()
{
while(!m_bCheckThreadStopFlag)
{
if (g_queueSerialCommandTypeInfo->size() > 0)
{
g_mutexSerialCommand->lock();
SerialCommandTypeInfo info = g_queueSerialCommandTypeInfo->dequeue();
g_mutexSerialCommand->unlock();
SerialCommandParamter p = info.parameter;
if (info.type == MONITORING_DIGITALINPUT)
{
m_pPickingController[p.objectIndex]->m_pDigitalInputController->monitoring();
}
}
QThread::msleep(20);
}
}
When executed, the following warning appears and the monitoring function cannot be called.
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QModbusRtuSerialMaster(0x2c56de33090), parent's thread is QThread(0x2c5670790b0), current thread is QThread(0x2c56ddb6290)
QObject::startTimer: Timers can only be used with threads started with QThread
How should I do?
I try to parallelize calculations and write the results in order to a vector.
I'm using a std::async. The program works perfectly with a short pause (ref. the commented line). Without this pause, the program crashes.
The Code ( in an online IDE to modify & test the MCVE :
class AsyncDispatcher {
public:
/* Data is a custom class with data that contains a ID.
* The object contains no
* links or pointers.*/
vector<Data> calcResult(vector<Params> params) {
vector<Data> result;
vector<std::future<NumberedData>> futures;
threadID = 0;
for (auto param : params) {
futures.push_back(((std::async(std::launch::async, [&]() {
this_thread::sleep_for(chrono::milliseconds(100)); // not workable without this command
//^^^^^^^^^^^^^^^^^^^^^^^^^
return NumberedData(filler.createData(param, threadID), threadID);
}))));
threadID++;
}
for (auto& f : futures) {
result.push_back(f.get().data);
}
return result;
}
AsyncDispatcher() : threadID(0) {}
private:
int threadID;
Filler filler;//fills an Data with numbers
};
I am trying to implement a thread-safe solution to keep a count of successful tasks that have been completed, which will ultimately get bound to label displayed on the UI. However, when I use the AtomicInteger below it locks up my UI when the tasks start running, however, if I remove all AtomicInteger refs everything works fine. Is there a non-blocking, thread-safe way which this can be accomplished?
public void handleSomeButtonClick(){
if(!dataModel.getSomeList().isEmpty()) {
boolean unlimited = false;
int count = 0;
AtomicInteger successCount = new AtomicInteger(0);
if(countSelector.getValue().equalsIgnoreCase("Unlimited"))
unlimited = true;
else
count = Integer.parseInt(countSelector.getValue());
while(unlimited || successCount.get() < count) {
Task task = getSomeTask();
taskExecutor.submit(task);
task.setOnSucceeded(event -> {
if (task.getValue())
log.info("Successfully Completed Task | Total Count: " + successCount.incrementAndGet());
else
log.error("Failed task");
});
}
}
}
Your loop waits for a certain number of tasks to be completed. It may even be an infinite loop.
This is not a good idea:
You block the calling thread which seems to be the JavaFX application thread.
You don't have any control of how many tasks are submitted. count could be 3, but since you only schedule the tasks in the loop, 1000 or more tasks could be created&scheduled before the first one completes.
Furthermore if you use onSucceeded/onFailed, you don't need to use AtomicInteger or any similar kind of synchronisation, since those handlers all run on the JavaFX application thread.
Your code could be rewritten like this:
private int successCount;
private void scheduleTask(final boolean unlimited) {
Task task = getSomeTask();
task.setOnSucceeded(event -> {
// cannot get a Boolean from a raw task, so I assume the task is successfull iff no exception happens
successCount++;
log.info("Successfully Completed Task | Total Count: " + successCount);
if (unlimited) {
// submit new task, if the number of tasks is unlimited
scheduleTask(true);
}
});
// submit new task on failure
task.setOnFailed(evt -> scheduleTask(unlimited));
taskExecutor.submit(task);
}
public void handleSomeButtonClick() {
if(!dataModel.getSomeList().isEmpty()) {
successCount = 0;
final boolean unlimited;
final int count;
if(countSelector.getValue().equalsIgnoreCase("Unlimited")) {
unlimited = true;
count = 4; // set limit of number of tasks submitted to the executor at the same time
} else {
count = Integer.parseInt(countSelector.getValue());
unlimited = false;
}
for (int i = 0; i < count; i++) {
scheduleTask(unlimited);
}
}
}
Note: This code runs the risk of handleButtonClick being clicked multiple times before the previous tasks have been completed. You should either prevent scheduling new tasks before the old ones are completed or use some reference type containing an int instead for the count, create this object in handleSomeButtonClick and pass this object to scheduleTask.
Your UI lock up means you do the counting(successCount.get() < count) in your FX application thread. I cannot understand why you keep submit the task in the while loop,
which one do you want to do? (1) start X(e.g. 10) task and count how many task is success. or (2) just keep starting new task and see the count go up.
if(2) then run the whole while loop in a background thread, update the UI in a Platform->runlater().
if(1) use the Future / CompletableFuture, or more powerful version Future in 3rd party package like vavr.
Your problem is future.get() block and wait for result.
This will be simple if you use Vavr library.
Because it can attach a code to its future which run automatically when success or fail.
So you don't have to wait.
Here is a example which using Vavr's future.
CheckedFunction0<String> thisIsATask = () -> {
if ( /*do something*/ ){
throw new Exception("Hey");
}
return "ABC";
};
List<Future<String>> futureList = new ArrayList<>();
for (int x = 0; x < 10; x++) {
futureList.add(Future.of(getExecutorService(), thisIsATask));
}
futureList.forEach((task) -> {
// This will run if success
task.onSuccess(s -> s.equals("ABC") ? Platform.runLater(()->UpdateCounter()) : wtf());
// Your get the exception if it is fail;
task.onFailure(e -> e.printStackTrace());
// task.onComplete() will run on any case when complete
});
This is not blocking, the code at onSucess onFailure or onComplete will run when the task is finish or an exception is catch.
Note: Future.of will use the executorService you pass in to run each task at new thread, the code you provide at onSuccess will continue to run at that thread once the task is done so if you calling javafx remember the Platform.runLater()
Also if you want to run something when all task is finish, then
// the code at onComplete will run when tasks all done
Future<Seq<String>> all = Future.sequence(futureList);
all.onComplete((i) -> this.btnXYZ.setDisable(false));
I have a call() method in my code, which based on certain conditions calls specific methods :
call(){
if(a){
methodA();
}
if(b){
methodB();
}
if(c){
methodC();
}
}
In the above scenario, I want to limit concurrent executions for methodC.
How can this be achieved?
What you need here is a Semaphore construct (check the bouncer/night club specification in the example).
// Create the semaphore with 3 slots, where 3 are available.
var bouncer = new Semaphore(3, 3);
call(){
if(a){
methodA();
}
if(b){
methodB();
}
if(c){
// Let a thread execute only after acquiring access (a semaphore to be released).
Bouncer.WaitOne();
methodC();
// This thread is done. Let someone else go for it
Bouncer.Release(1);
}
}
If you want to limit the number of concurrent executions to at most one at a time, then you should use a Lock. In Java it should look like:
final Lock lock = new ReentrantLock();
call() {
if(a) {
methodA();
}
if(b) {
methodB();
}
if(c) {
lock.lock();
try {
methodC();
} finally {
lock.unlock();
}
}
}
If you want to limit the number of concurrent executions to more than one at a time, you can use a Semaphore; here CONCURRENT_CALLS_ALLOWED is an int.
final Semaphore semaphore = new Semaphore(CONCURRENT_CALLS_ALLOWED);
call() {
if(a) {
methodA();
}
if(b) {
methodB();
}
if(c) {
semaphore.aquire();//throws checked exception
try {
methodC();
} finally {
semaphore.release();
}
}
}
I got CyclicBarrier code from oracle page to understand it more. I modified it and now having one doubt.
Below code doesn't terminate but If I uncomment Thread.sleep condition, It works fine.
import java.util.Arrays;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
class Solver {
final int N;
final float[][] data;
boolean done = false;
final CyclicBarrier barrier;
class Worker implements Runnable {
int myRow;
Worker(int row) {
myRow = row;
}
public void run() {
while (!done) {
processRow(myRow);
try {
barrier.await();
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
System.out.println("Run finish for " + Thread.currentThread().getName());
}
private void processRow(int row) {
float[] rowData = data[row];
for (int i = 0; i < rowData.length; i++) {
rowData[i] = 1;
}
/*try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
done = true;
}
}
public Solver(float[][] matrix) {
data = matrix;
N = matrix.length;
barrier = new CyclicBarrier(N, new Runnable() {
public void run() {
for (int i = 0; i < data.length; i++) {
System.out.println("Data " + Arrays.toString(data[i]));
}
System.out.println("Completed:");
}
});
for (int i = 0; i < N; ++i)
new Thread(new Worker(i), "Thread "+ i).start();
}
}
public class CyclicBarrierTest {
public static void main(String[] args) {
float[][] matrix = new float[5][5];
Solver solver = new Solver(matrix);
}
}
Why Thread.sleep is required in above code?
I've not run your code but there may be a race condition, here is a scenario that reveals it:
you start the first thread, it runs during a certain amount of time sufficient for it to finish the processRow method call so it sets done to true and then waits on the barrier,
the other threads start but they see that all is "done" so they don't enter the loop and they'll never wait on the barrier, and end directly
the barrier will never be activated as only one of the N threads has reached it
deadlock
Why it is working with the sleep:
when one of the thread starts to sleep it lets the other threads work before marking the work as "done"
the other threads have enough time to work and can themselves reach the barrier
2 seconds is largely enough for 5 threads to end a processing that should not last longer than 10ms
But note that if your system is ovrerloaded it could too deadlock:
the first thread starts to sleep
the OS scheduler lets another application work during more than 2 seconds
the OS scheduler comes back to your application and the threads scheduler chooses the first thread again and lets it terminate, setting done to true
and here again the first scenario => deadlock too
And a possible solution (sorry not tested):
change your while loops for do/while loops:
do
{
processRow(myRow);
...
}
while (!done);