Skip to content

Work Control

ZjzMisaka edited this page Nov 21, 2024 · 53 revisions

Quick Start

Summary

Pause(...)
Pause the pool or works, work id(s) can be used as a parameter.
Resume(...)
Resume the pool or works, work id(s) can be used as a parameter.
Stop(...)
Stop the pool or works, work id(s) and force stop flag can be used as parameters.
Wait[Async](...)
Block and wait for the pool or works to complete, work id(s) can be used as a parameter.
Fetch[Async](...)
Block and get the result of work, work id(s) or predicate function and remove result from storage after fetch flag can be used as parameters.
Cancel(...)
Cancel the works which hasn't been started, work id(s) can be used as a parameter.
PauseIfRequested()
Insert a pause point into the work logic.
StopIfRequested(...)
Insert a stop point into the work logic, function that is executed before the stop process can be used as a parameter.
CheckIfRequestedStop()
Insert a check point into the work logic to see if a stop has been requested by calling Stop(...).

Example Code

PowerPool powerPool = new PowerPool();

string id0 = powerPool.QueueWorkItem(() =>
{
    while (true)
    {
        powerPool.PauseIfRequested();
        if (powerPool.CheckIfRequestedStop())
        {
            return true;
        }
        // Do something
    }
});
Task<ExecuteResult<bool>> resultTask = powerPool.FetchAsync<bool>(id0);
powerPool.Pause();
powerPool.Resume();
powerPool.Stop();
powerPool.Wait();
ExecuteResult<bool> result = await resultTask;

string id1 = powerPool.QueueWorkItem(() =>
{
    while (true)
    {
        powerPool.PauseIfRequested();
        powerPool.StopIfRequested();
        // Do something
    }
});
powerPool.Pause(id1);
powerPool.Resume(id1);
powerPool.Stop(id1);
powerPool.Wait(id1);

Detailed Explanations

Pause Resume Stop

Due to the unpredictable behavior caused by Thread.Abort, Thread.Suspend and Thread.Resume, they have been deprecated in newer .NET versions, therefore the PowerThreadPool library also does not use these API to control a thread.
PowerThreadPool encapsulates operations of CancellationTokenSource and ManualResetEvent, implementing cooperative thread pool and work control.

Cooperative control

PauseIfRequested(), StopIfRequested(...), or CheckIfRequestedStop() can be inserted into work logic (for example, in loops or at nodes of time-consuming logic) to manage execution flow. When Pause, Resume, or Stop functions are called, the logic will pause, resume, or stop at the desired position. Specifying the work ID as the argument for these functions ensures that other works are not affected.

  • PauseIfRequested(): Call this function inside the work logic where you want to pause when user call Pause(...)
  • StopIfRequested(Func<bool> beforeStop = null): Call this function inside the work logic where you want to stop when user call Stop(...)
    To exit the logic, the function will throw a PowerThreadPool.Exceptions.WorkStopException. WorkStopException is only used to stop the work, do not catch it because PowerThreadPool will handle it.
    If you want to do something before stop (for example, if you have some unmanaged resources that need to be released before exiting) or prevent stopping in some case, it is recommended to use param beforeStop.
    If you do not want to exit the logic in this way , it is you can also use CheckIfRequestedStop.
  • CheckIfRequestedStop(): Call this function inside the work logic where you want to check if requested stop (if user call Stop(...))
    When returning true, you can perform some pre operations (such as releasing unmanaged resources) and then safely exit the logic. You can even choose to ignore the stop request depending on the situation.
    Stopping the work in this manner will not trigger the WorkStopped event, but will instead be regarded as the work having been completed normally.
string workID = powerPool.QueueWorkItem(() =>
{
    while (true)
    {
        powerPool.PauseIfRequested();
        powerPool.StopIfRequested();
        // Do something
    }
});
string workID = powerPool.QueueWorkItem(() =>
{
    while (true)
    {
        powerPool.PauseIfRequested();
        powerPool.StopIfRequested(() =>
        {
            // Do something before return
            if (/* You want to continue stopping */)
            {
                return true;
            }
            else
            {
                return false;
            }
        });
        // Do something
    }
});
string workID = powerPool.QueueWorkItem(() =>
{
    while (true)
    {
        if (powerPool.CheckIfRequestedStop())
        {
            // Do something before return
            return;
        }
        // Do something
    }
});
powerPool.Pause();
powerPool.Pause(workID);
powerPool.Pause(workIDList);
powerPool.Resume();
powerPool.Resume(workID);
powerPool.Resume(workIDList);
powerPool.Stop();
powerPool.Stop(workID);
powerPool.Stop(workIDList);

Force Stop

When the Stop function accepts a boolean argument (i.e., forceStop) as true, it can stop the thread even if StopIfRequested(...) is not inserted into the work logic, because the Thread.Interrupt() will be called. The thread pool will immediately terminate the thread after catching a ThreadInterruptedException.
Although this approach is safer than Thread.Abort, from the perspective of the business logic, it can still potentially lead to unpredictable results and cannot guarantee the time consumption of exiting the thread, therefore you should avoid using force stop as much as possible.
The threads stopped in this way will be destroyed and not preserved.

string workID = powerPool.QueueWorkItem(() =>
{
    while (true)
    {
        // Do something
    }
});
powerPool.Stop(true);
powerPool.Stop(workID, true);
powerPool.Stop(workIDList, true);

Wait

The calling thread can be blocked until all of the works terminate.
The work ID can be used as the argument for the Wait, ensuring that other works are not affected.
The asynchronous counterpart to Wait is WaitAsync.

powerPool.Wait();
powerPool.Wait(workID);
powerPool.Wait(workIDList);

Fetch

The Fetch function can be used to get the result of work. After calling, the ExecuteResult will be returned upon completion of the work.

Fetch by work id(s)

A workID or workIDList parameter is required.
Typically, the Fetch function is used to get the result of work that has either not started or is currently in progress.
This function is blocking, meaning that it will halt the execution of the calling thread until the specified work items are completed and their results are returned.
For non-blocking behavior, the FetchAsync function can be used. This function allows the result of the work to be obtained asynchronously, without interrupting the execution of the calling thread.

Fetch by predicate function

A predicate function parameter is required. It will search for a collection of matching results from the already existing results based on the predicate function. The predicate function is a function that takes an ExecuteResult<TResult> as a parameter and returns a bool value, similar to Where in LINQ.
Since it filters from pre-existing results, it will not cause blocking, and therefore, there is no asynchronous version.

The asynchronous counterpart to Fetch is FetchAsync.

powerPool.Fetch(workID);
powerPool.Fetch(workIDList);
powerPool.Fetch<int>(x => x.Result >= 3);

Get results of completed work

By default, to prevent memory overflow, the thread pool does not store the results of work executions. If you wish to retain the results of completed work, you should set the WorkOption.ShouldStoreResult to true, which instructs the thread pool to save the results after work completion.

Clear the saved execution results

There are three ways to clear saved execution results.

  • Set PowerPoolOption.ClearResultStorageWhenPoolStart to true. This will clear the saved execution results when the thread pool enters the running state again.
PowerPool powerPool = new PowerPool(new PowerPoolOption() { ClearResultStorageWhenPoolStart = true });
  • Set the optional parameter of Fetch function to true (Fetch(workID, true)), then the execution result of the work corresponding to the workID will be cleared when Fetch is called.
powerPool.Fetch(workID, true);
powerPool.Fetch(workIDList, true);
  • Call PowerPool.ClearResultStorage(...) directly.
powerPool.ClearResultStorage();
powerPool.ClearResultStorage(workID);
powerPool.ClearResultStorage(workIDList);

Cancel

The Cancel function can be used to cancel the works which hasn't been started.
The work ID can be used as the argument for the Cancel, ensuring that other works are not affected.

powerPool.Cancel();
powerPool.Cancel(workID);
powerPool.Cancel(workIDList);
Clone this wiki locally