Help to settle the flow. I will not provide the code, I just need an idea.
There is some event that triggers a thread. I need to make every launch of this thread complete the execution of the previous instance.
Framework 4.0 (without async / await)
Help to settle the flow. I will not provide the code, I just need an idea.
There is some event that triggers a thread. I need to make every launch of this thread complete the execution of the previous instance.
Framework 4.0 (without async / await)
The “old” and most clumsy way is to create a stream and kill it if necessary. However, potentially this is a very dangerous way, because, for example, a stream can leave general data in an inconsistent state or refuse to complete at all.
class Program { private static Thread _thread; static void Main(string[] args) { Console.ReadLine(); TriggerEventOld(); Console.ReadLine(); TriggerEventOld(); // дождемся завершения _thread.Join(); } private static void TriggerEventOld() { if (_thread != null) { _thread.Abort(); // корректнее будет дождаться завершения _thread.Join(); } _thread = new Thread(Foo); _thread.Start(); } private static void Foo() { Console.WriteLine("Foo started"); try { for (int i = 0; i < 10; i++) { Thread.Sleep(500); } } catch (ThreadAbortException) { Console.WriteLine("Foo aborted"); } Console.WriteLine("Foo ended"); } } It is better not to contact Abort() , and use the "soft" thread termination using ManualResetEventSlim , which will serve as a completion flag. This method has one drawback - if you run a synchronous code that can respond for a long time (for example, reading a file or a request to the network), you will have to wait a long time for the stream to complete upon a new launch.
class Program { private static Thread _thread; private static ManualResetEventSlim _reset = new ManualResetEventSlim(); static void Main(string[] args) { Console.ReadLine(); TriggerEventOld(); Console.ReadLine(); TriggerEventOld(); // дождемся завершения _thread.Join(); } private static void TriggerEventOld() { if (_thread != null) { _reset.Set(); // корректнее будет дождаться завершения _thread.Join(); } _thread = new Thread(Foo); _reset.Reset(); _thread.Start(); } private static void Foo() { Console.WriteLine("Foo started"); for (int i = 0; i < 10; i++) { if (_reset.IsSet) { Console.WriteLine("Foo canceled"); break; } Thread.Sleep(500); } if (!_reset.IsSet) { Console.WriteLine("Foo ended"); } } } But since you are using .NET 4.0, then TPL is available to you. In this case, it will be better to use Task and CancellationToken , with them you can make a "soft" task stop. The advantage of the new model is that almost all asynchronous methods support stopping using CancellationToken , so the code will respond to completion more quickly.
class Program { private static Task _task; private static CancellationTokenSource _cts; static void Main(string[] args) { Console.ReadLine(); TriggerEventNew(); Console.ReadLine(); TriggerEventNew(); // дождемся завершения; // этот вызов может выбросить исключение, // если внутри возникнет необработанное исключение _task.Wait(); } private static void TriggerEventNew() { if (_task != null) { _cts.Cancel(); // корректнее будет дождаться завершения; // этот вызов может выбросить исключение, // если внутри возникнет необработанное исключение _task.Wait(); } // пересоздаем каждый раз, поскольку это одноразовый объект _cts = new CancellationTokenSource(); _task = Task.Factory.StartNew(() => FooNew(_cts.Token), _cts.Token); } private static void FooNew(CancellationToken token) { Console.WriteLine("FooNew started"); for (int i = 0; i < 10; i++) { if (token.IsCancellationRequested) { Console.WriteLine("FooNew aborted"); break; } Thread.Sleep(500); } if (!token.IsCancellationRequested) { Console.WriteLine("FooNew ended"); } } } If you use token.ThrowIfCancellationRequested() , do not forget to wrap the code in a try/catch with checking tokens:
try { ... token.ThrowIfCancellationRequested(); ... } catch (OperationCanceledException e) { if (e.CancellationToken != token) { // отмена была вызвана не нами, бросаем исключение throw; } } CancellationToken can be used not only with tasks, but also with threads. - Pavel MayorovStop() . - Pavel MayorovIn the end, I came up with my own solution. If it is developed a little, it can be adapted for other tasks. But this functionality is enough for me.
public class TaskQueue { Task _worker; Queue<Action> _queue; int _maxTasks; bool _deleteOld; object _lock = new object(); public TaskQueue(int maxTasks, bool deleteOld = true) { if (maxTasks < 1) throw new ArgumentException("TaskQueue: максимальное число задач должно быть больше 0"); _maxTasks = maxTasks; _deleteOld = deleteOld; _queue = new Queue<Action>(maxTasks); } public bool Add(Action action) { if (_queue.Count() < _maxTasks) { _queue.Enqueue(action); DoWorkAsync(); return true; } if (_deleteOld) { _queue.Dequeue(); return Add(action); } return false; } void DoWorkAsync() { if(_queue.Count>0) _worker = Task.Factory.StartNew(DoWork); } void DoWork() { lock (_lock) { if (_queue.Count > 0) { _queue.Dequeue().Invoke(); DoWork(); } } } } Everything is simple here. There is a queue of tasks of a certain size and a flag that says whether to delete old tasks in the event of a queue overflow. That is, I add the task, it tries to add to the queue. If there is free space in the queue, it is added, if not, then it is either not added, or it removes the old task (which is next to be executed) from the queue. Next, a queue handler is started, which, if another task is not executed, takes the task from the queue and starts it. Upon completion, it runs recursively.
Task.Factory.StartNew() call in DoWork() . Calling someone else's code inside a lock can be a problem. Etc. - andreychaTask.Factory.StartNew(_queue.Dequeue()) in DoWork() is the actual launch of the Action itself, which the programmer passed to it. If you have not noticed, in the Add parameters is not Task , but Action . What problems can result in calling someone else's code inside a lock? Need specifics. - iRumbaStartNew in DoWork . Corrected. - iRumbaFramework 4.0 (без async / await) - iRumbaSource: https://ru.stackoverflow.com/questions/573579/
All Articles
Taskclass andCancellationToken- rdorn