Trong một chủ đề, tôi tạo một số System.Threading.Task
và bắt đầu mỗi nhiệm vụ.
Khi tôi thực hiện .Abort()
để giết luồng, các tác vụ không bị hủy bỏ.
Làm thế nào tôi có thể truyền tải các .Abort()
nhiệm vụ của tôi?
Trong một chủ đề, tôi tạo một số System.Threading.Task
và bắt đầu mỗi nhiệm vụ.
Khi tôi thực hiện .Abort()
để giết luồng, các tác vụ không bị hủy bỏ.
Làm thế nào tôi có thể truyền tải các .Abort()
nhiệm vụ của tôi?
Câu trả lời:
Bạn không thể. Nhiệm vụ sử dụng chủ đề nền từ nhóm chủ đề. Ngoài ra, hủy bỏ các chủ đề bằng phương pháp Abort không được khuyến khích. Bạn có thể xem bài đăng trên blog sau đây giải thích cách hủy tác vụ phù hợp bằng cách sử dụng mã thông báo hủy. Đây là một ví dụ:
class Program
{
static void Main()
{
var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
Task.Factory.StartNew(() =>
{
while (true)
{
// do some heavy work here
Thread.Sleep(100);
if (ct.IsCancellationRequested)
{
// another thread decided to cancel
Console.WriteLine("task canceled");
break;
}
}
}, ct);
// Simulate waiting 3s for the task to complete
Thread.Sleep(3000);
// Can't wait anymore => cancel this task
ts.Cancel();
Console.ReadLine();
}
}
Task.Wait(TimeSpan / int)
để đưa ra thời hạn (dựa trên thời gian) từ bên ngoài.
Task
? Một cái gì đó như : public int StartNewTask(Action method)
. Bên trong StartNewTask
phương thức tôi tạo mới Task
bằng : Task task = new Task(() => method()); task.Start();
. Vì vậy, làm thế nào tôi có thể quản lý CancellationToken
? Tôi cũng muốn biết nếu Thread
tôi cần phải thực hiện logic để kiểm tra xem có một số Nhiệm vụ vẫn đang treo hay không và vì vậy hãy giết chúng khi nào Form.Closing
. Với Threads
tôi sử dụng Thread.Abort()
.
Việc hủy bỏ một Nhiệm vụ có thể dễ dàng nếu bạn nắm bắt được luồng mà tác vụ đang chạy. Dưới đây là một mã ví dụ để chứng minh điều này:
void Main()
{
Thread thread = null;
Task t = Task.Run(() =>
{
//Capture the thread
thread = Thread.CurrentThread;
//Simulate work (usually from 3rd party code)
Thread.Sleep(1000);
//If you comment out thread.Abort(), then this will be displayed
Console.WriteLine("Task finished!");
});
//This is needed in the example to avoid thread being still NULL
Thread.Sleep(10);
//Cancel the task by aborting the thread
thread.Abort();
}
Tôi đã sử dụng Task.Run () để hiển thị trường hợp sử dụng phổ biến nhất cho trường hợp này - sử dụng sự thoải mái của Nhiệm vụ với mã đơn luồng cũ, không sử dụng lớp CancellingTokenSource để xác định xem có nên hủy bỏ hay không.
CancellationToken
hỗ trợ ...
CancellationToken
hoặc thậm chí các giải pháp đơn giản hơn là không có điều kiện chủng tộc nên được xem xét. Mã ở trên chỉ minh họa phương thức, không phải khu vực sử dụng.
thread
biến cục bộ). Trong mã của bạn, sau đó bạn có thể hủy bỏ chủ đề chính, đó không phải là điều bạn thực sự muốn. Có lẽ một kiểm tra nếu các chủ đề là giống nhau trước khi phá thai sẽ là một suy nghĩ tốt để có, nếu bạn khăng khăng phá thai
Giống như bài viết này gợi ý, điều này có thể được thực hiện theo cách sau:
int Foo(CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// compute-bound work here
}
}
Mặc dù nó hoạt động, nhưng không nên sử dụng cách tiếp cận như vậy. Nếu bạn có thể kiểm soát mã thực thi trong tác vụ, tốt hơn hết là bạn nên xử lý hủy hợp lý.
Loại điều này là một trong những lý do hậu cần tại sao Abort
bị phản đối. Đầu tiên và quan trọng nhất, không sử dụng Thread.Abort()
để hủy bỏ hoặc dừng một chủ đề nếu có thể. Abort()
chỉ nên được sử dụng để giết chết một chủ đề không đáp ứng với các yêu cầu hòa bình hơn để dừng lại một cách kịp thời.
Điều đó đang được nói, bạn cần cung cấp một chỉ báo hủy được chia sẻ rằng một luồng sẽ đặt và chờ trong khi luồng khác kiểm tra định kỳ và thoát ra một cách duyên dáng. .NET 4 bao gồm một cấu trúc được thiết kế dành riêng cho mục đích này , CancellationToken
.
Bạn không nên cố gắng làm điều này trực tiếp. Thiết kế các nhiệm vụ của bạn để làm việc với CancellingToken và hủy chúng theo cách này.
Ngoài ra, tôi cũng khuyên bạn nên thay đổi chủ đề chính của mình để hoạt động thông qua CancellingToken. Gọi điện Thread.Abort()
là một ý tưởng tồi - nó có thể dẫn đến nhiều vấn đề rất khó chẩn đoán. Thay vào đó, luồng đó có thể sử dụng cùng một Hủy mà các tác vụ của bạn sử dụng - và CancellationTokenSource
có thể được sử dụng tương tự để kích hoạt hủy tất cả các tác vụ và luồng chính của bạn.
Điều này sẽ dẫn đến một thiết kế đơn giản hơn và an toàn hơn.
Để trả lời câu hỏi của Prerak K về cách sử dụng CancellingTokens khi không sử dụng phương thức ẩn danh trong Task.Factory.StartNew (), bạn chuyển CancellingToken làm tham số cho phương thức bạn bắt đầu với StartNew (), như trong ví dụ MSDN ở đây .
ví dụ
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task.Factory.StartNew( () => DoSomeWork(1, token), token);
static void DoSomeWork(int taskNum, CancellationToken ct)
{
// Do work here, checking and acting on ct.IsCancellationRequested where applicable,
}
Tôi sử dụng một cách tiếp cận hỗn hợp để hủy bỏ một nhiệm vụ.
Kiểm tra một ví dụ dưới đây:
private CancellationTokenSource taskToken;
private AutoResetEvent awaitReplyOnRequestEvent = new AutoResetEvent(false);
void Main()
{
// Start a task which is doing nothing but sleeps 1s
LaunchTaskAsync();
Thread.Sleep(100);
// Stop the task
StopTask();
}
/// <summary>
/// Launch task in a new thread
/// </summary>
void LaunchTaskAsync()
{
taskToken = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
try
{ //Capture the thread
runningTaskThread = Thread.CurrentThread;
// Run the task
if (taskToken.IsCancellationRequested || !awaitReplyOnRequestEvent.WaitOne(10000))
return;
Console.WriteLine("Task finished!");
}
catch (Exception exc)
{
// Handle exception
}
}, taskToken.Token);
}
/// <summary>
/// Stop running task
/// </summary>
void StopTask()
{
// Attempt to cancel the task politely
if (taskToken != null)
{
if (taskToken.IsCancellationRequested)
return;
else
taskToken.Cancel();
}
// Notify a waiting thread that an event has occurred
if (awaitReplyOnRequestEvent != null)
awaitReplyOnRequestEvent.Set();
// If 1 sec later the task is still running, kill it cruelly
if (runningTaskThread != null)
{
try
{
runningTaskThread.Join(TimeSpan.FromSeconds(1));
}
catch (Exception ex)
{
runningTaskThread.Abort();
}
}
}
Nhiệm vụ có hỗ trợ hạng nhất để hủy qua mã thông báo hủy . Tạo các nhiệm vụ của bạn bằng các mã thông báo hủy và hủy các tác vụ thông qua các tác vụ này một cách rõ ràng.
Bạn có thể sử dụng a CancellationToken
để kiểm soát xem tác vụ có bị hủy hay không. Bạn đang nói về việc hủy bỏ nó trước khi nó bắt đầu ("đừng bận tâm, tôi đã làm điều này"), hoặc thực sự làm gián đoạn nó ở giữa? Nếu trước đây, CancellationToken
có thể hữu ích; nếu sau này, có lẽ bạn sẽ cần phải thực hiện cơ chế "bảo lãnh" của riêng mình và kiểm tra các điểm thích hợp trong quá trình thực thi nhiệm vụ xem bạn có nên thất bại nhanh không (bạn vẫn có thể sử dụng CancellingToken để giúp bạn, nhưng thủ công hơn một chút).
MSDN có một bài viết về việc hủy bỏ Nhiệm vụ: http://msdn.microsoft.com/en-us/l Library / dd997394.aspx
Tác vụ đang được thực thi trên ThreadPool (ít nhất, nếu bạn đang sử dụng nhà máy mặc định), vì vậy việc hủy bỏ luồng có thể ảnh hưởng đến các tác vụ. Để hủy bỏ các nhiệm vụ, xem Hủy bỏ nhiệm vụ trên msd.
Tôi đã thử CancellationTokenSource
nhưng tôi không thể làm điều này. Và tôi đã làm điều này với cách riêng của tôi. Và nó hoạt động.
namespace Blokick.Provider
{
public class SignalRConnectProvider
{
public SignalRConnectProvider()
{
}
public bool IsStopRequested { get; set; } = false; //1-)This is important and default `false`.
public async Task<string> ConnectTab()
{
string messageText = "";
for (int count = 1; count < 20; count++)
{
if (count == 1)
{
//Do stuff.
}
try
{
//Do stuff.
}
catch (Exception ex)
{
//Do stuff.
}
if (IsStopRequested) //3-)This is important. The control of the task stopping request. Must be true and in inside.
{
return messageText = "Task stopped."; //4-) And so return and exit the code and task.
}
if (Connected)
{
//Do stuff.
}
if (count == 19)
{
//Do stuff.
}
}
return messageText;
}
}
}
Và một lớp khác của phương thức gọi:
namespace Blokick.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MessagePerson : ContentPage
{
SignalRConnectProvider signalR = new SignalRConnectProvider();
public MessagePerson()
{
InitializeComponent();
signalR.IsStopRequested = true; // 2-) And this. Make true if running the task and go inside if statement of the IsStopRequested property.
if (signalR.ChatHubProxy != null)
{
signalR.Disconnect();
}
LoadSignalRMessage();
}
}
}
Bạn có thể hủy bỏ một tác vụ như một luồng nếu bạn có thể khiến tác vụ được tạo trên luồng của chính nó và gọi đối tượng Abort
của nó Thread
. Theo mặc định, một tác vụ chạy trên một chuỗi nhóm luồng hoặc luồng gọi - không phải trong số đó bạn thường muốn hủy bỏ.
Để đảm bảo tác vụ có chủ đề riêng, hãy tạo một lịch trình tùy chỉnh bắt nguồn từ TaskScheduler
. Trong quá trình thực hiện của bạn QueueTask
, tạo một luồng mới và sử dụng nó để thực thi tác vụ. Sau đó, bạn có thể hủy bỏ luồng, điều này sẽ khiến tác vụ hoàn thành trong trạng thái bị lỗi với a ThreadAbortException
.
Sử dụng lịch trình tác vụ này:
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException(); // Unused
protected override bool NotSupportedException(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException(); // Unused
}
Bắt đầu nhiệm vụ của bạn như thế này:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Sau đó, bạn có thể hủy bỏ với:
scheduler.TaskThread.Abort();
Lưu ý rằng cảnh báo về việc hủy bỏ một chủ đề vẫn được áp dụng:
Các
Thread.Abort
phương pháp nên được sử dụng một cách thận trọng. Đặc biệt, khi bạn gọi nó để hủy bỏ một luồng khác với luồng hiện tại, bạn không biết mã nào đã thực thi hoặc không thực thi được khi ThreadAdortException bị ném, bạn cũng không thể chắc chắn về trạng thái của ứng dụng hoặc bất kỳ trạng thái ứng dụng và ứng dụng nào của bạn rằng nó có trách nhiệm bảo quản. Ví dụ, việc gọiThread.Abort
có thể ngăn các nhà xây dựng tĩnh thực thi hoặc ngăn chặn việc giải phóng các tài nguyên không được quản lý.
Thread.Abort
là không được hỗ trợ trên .NET Core. Cố gắng sử dụng nó sẽ dẫn đến một ngoại lệ: System.Pl platformNotSupportedException: Hủy bỏ luồng không được hỗ trợ trên nền tảng này. Một cảnh báo thứ ba là SingleThreadTaskScheduler
không thể sử dụng hiệu quả với các nhiệm vụ theo kiểu hứa hẹn, nói cách khác với các nhiệm vụ được tạo với async
các đại biểu. Ví dụ, một nhúng await Task.Delay(1000)
chạy trong không có luồng, vì vậy nó không bị ảnh hưởng là các sự kiện luồng.