ในหัวข้อฉันสร้างบางอย่าง System.Threading.Task
และเริ่มต้นแต่ละงาน
เมื่อฉันทำ.Abort()
เพื่อฆ่าเธรดงานจะไม่ถูกยกเลิก
ฉันจะส่ง.Abort()
ไปยังงานของฉันได้อย่างไร
ในหัวข้อฉันสร้างบางอย่าง System.Threading.Task
และเริ่มต้นแต่ละงาน
เมื่อฉันทำ.Abort()
เพื่อฆ่าเธรดงานจะไม่ถูกยกเลิก
ฉันจะส่ง.Abort()
ไปยังงานของฉันได้อย่างไร
คำตอบ:
คุณทำไม่ได้ งานใช้เธรดพื้นหลังจากกลุ่มเธรด นอกจากนี้ไม่แนะนำให้ยกเลิกการใช้เธรดด้วยวิธีการยกเลิก คุณสามารถดูโพสต์บล็อกต่อไปนี้ซึ่งอธิบายวิธีที่เหมาะสมในการยกเลิกงานโดยใช้โทเค็นการยกเลิก นี่คือตัวอย่าง:
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)
เพื่อกำหนดเส้นตาย (ตามเวลา) จากภายนอก
Task
? public int StartNewTask(Action method)
สิ่งที่ชอบ: ข้างในStartNewTask
วิธีที่ฉันสร้างใหม่Task
โดย: Task task = new Task(() => method()); task.Start();
. ดังนั้นฉันจะจัดการได้CancellationToken
อย่างไร ฉันต้องการทราบด้วยเช่นกันว่าThread
ฉันต้องใช้ตรรกะเพื่อตรวจสอบว่ามีงานบางอย่างที่ยังแขวนอยู่และฆ่าพวกเขาเมื่อForm.Closing
ใด ด้วยความที่ผมใช้Threads
Thread.Abort()
การยกเลิกงานนั้นเป็นไปได้อย่างง่ายดายหากคุณดักจับเธรดที่งานกำลังทำงานอยู่นี่คือตัวอย่างโค้ดที่แสดงให้เห็นถึงสิ่งนี้:
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();
}
ฉันใช้ Task.Run () เพื่อแสดงกรณีการใช้งานที่พบบ่อยที่สุด - ใช้ความสะดวกสบายของ Tasks ด้วยโค้ดเดี่ยวเธรดเก่าซึ่งไม่ได้ใช้คลาส CancellationTokenSource เพื่อพิจารณาว่าควรยกเลิกหรือไม่
CancellationToken
สนับสนุน ...
CancellationToken
ควรพิจารณาโซลูชันที่เรียบง่ายกว่าหรือปราศจากเงื่อนไขการแข่งขัน รหัสข้างต้นแสดงให้เห็นถึงวิธีการเท่านั้นไม่ใช่พื้นที่การใช้งาน
thread
ตัวแปรท้องถิ่น) ในรหัสของคุณคุณอาจสิ้นสุดการยกเลิกเธรดหลักซึ่งไม่ใช่สิ่งที่คุณต้องการ บางทีการตรวจสอบว่าเธรดเหมือนกันก่อนการยกเลิกจะเป็นความคิดที่ดีหรือไม่หากคุณยืนยันในการยกเลิก
เช่นเดียวกับโพสต์นี้แสดงให้เห็นสิ่งนี้สามารถทำได้ในวิธีต่อไปนี้:
int Foo(CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// compute-bound work here
}
}
แม้ว่ามันจะใช้งานได้ แต่ก็ไม่แนะนำให้ใช้วิธีการดังกล่าว หากคุณสามารถควบคุมรหัสที่ดำเนินการได้คุณควรดำเนินการยกเลิกอย่างเหมาะสม
เรื่องแบบนี้เป็นหนึ่งในเหตุผลด้านลอจิสติกว่าทำไมAbort
เลิกใช้แล้ว ก่อนอื่นอย่าใช้Thread.Abort()
เพื่อยกเลิกหรือหยุดเธรดถ้าเป็นไปได้ทั้งหมด Abort()
ควรใช้เพื่อฆ่าเธรดที่ไม่ตอบสนองต่อคำร้องขอสงบมากกว่าที่จะหยุดในเวลาที่เหมาะสมเท่านั้น
ที่ถูกกล่าวว่าคุณจะต้องให้ตัวบ่งชี้การยกเลิกที่ใช้ร่วมกันที่หนึ่งชุดด้ายและรอในขณะที่ด้ายอื่น ๆ ตรวจสอบเป็นระยะและออกอย่างสง่างาม .NET 4 CancellationToken
รวมถึงโครงสร้างที่ออกแบบมาเฉพาะสำหรับวัตถุประสงค์นี้
คุณไม่ควรพยายามทำสิ่งนี้โดยตรง ออกแบบงานของคุณให้ทำงานกับCancellationTokenและยกเลิกด้วยวิธีนี้
นอกจากนี้ฉันขอแนะนำให้เปลี่ยนเธรดหลักของคุณให้ทำงานผ่าน CancellationToken เช่นกัน การโทรThread.Abort()
เป็นแนวคิดที่ไม่ดี - อาจนำไปสู่ปัญหาต่าง ๆ ที่ยากต่อการวินิจฉัย แต่เธรดนั้นสามารถใช้การยกเลิกแบบเดียวกับที่งานของคุณใช้และสามารถใช้เธรดเดียวกันCancellationTokenSource
เพื่อเริ่มการยกเลิกงานทั้งหมดและเธรดหลักของคุณ
สิ่งนี้จะนำไปสู่การออกแบบที่เรียบง่ายและปลอดภัยยิ่งขึ้น
ในการตอบคำถามของ Prerak K เกี่ยวกับวิธีใช้ CancellationTokens เมื่อไม่ได้ใช้วิธีที่ไม่ระบุชื่อใน Task.Factory.StartNew () คุณจะผ่าน CancellationToken เป็นพารามิเตอร์ในวิธีที่คุณเริ่มต้นด้วย StartNew () ดังที่แสดงในตัวอย่าง MSDN ที่นี่
เช่น
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,
}
ฉันใช้วิธีการผสมเพื่อยกเลิกงาน
ชำระเงินตัวอย่างด้านล่าง:
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();
}
}
}
คุณสามารถใช้CancellationToken
เพื่อควบคุมว่างานจะถูกยกเลิกหรือไม่ คุณกำลังพูดถึงการยกเลิกก่อนที่จะเริ่มต้น ("ไม่เป็นไรฉันทำไปแล้ว") หรือขัดขวางการใช้งานจริงกลาง? หากอดีตCancellationToken
สามารถเป็นประโยชน์ได้ หากหลังคุณอาจต้องใช้กลไก "การประกันตัว" ของคุณเองและตรวจสอบจุดที่เหมาะสมในการปฏิบัติงานว่าคุณควรจะล้มเหลวอย่างรวดเร็ว (คุณยังสามารถใช้ CancellationToken เพื่อช่วยคุณได้ แต่เป็นคู่มือเล็กน้อย)
MSDN มีบทความเกี่ยวกับการยกเลิกงาน: http://msdn.microsoft.com/en-us/library/dd997396.aspx
งานกำลังถูกดำเนินการบน ThreadPool (อย่างน้อยถ้าคุณใช้ค่าเริ่มต้นจากโรงงาน) ดังนั้นการยกเลิกเธรดจะไม่ส่งผลกระทบต่องาน สำหรับการยกเลิกงานให้ดูที่การยกเลิกงานบน msdn
ฉันพยายามCancellationTokenSource
แต่ฉันทำไม่ได้ และฉันก็ทำสิ่งนี้ด้วยวิธีของฉันเอง และมันใช้งานได้
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;
}
}
}
และคลาสอื่นของการเรียกเมธอด:
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();
}
}
}
คุณสามารถยกเลิกงานเช่นเดียวกับเธรดหากคุณสามารถทำให้งานถูกสร้างบนเธรดของตนเองและเรียกAbort
ใช้งานThread
วัตถุ โดยค่าเริ่มต้นงานจะทำงานบนเธรดพูลเธรดหรือเธรดการโทร - โดยที่คุณไม่ต้องการยกเลิก
TaskScheduler
เพื่อให้แน่ใจว่างานที่ได้รับหัวข้อของตัวเองสร้างตารางเวลาที่กำหนดเองที่ได้มาจาก ในการนำไปใช้ของคุณQueueTask
ให้สร้างเธรดใหม่และใช้เพื่อเรียกใช้งาน ThreadAbortException
หลังจากนั้นคุณสามารถยกเลิกด้ายซึ่งจะทำให้งานที่จะเสร็จสมบูรณ์ในรัฐโทษฐานที่มี
ใช้ตัวกำหนดตารางงานนี้:
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
}
เริ่มงานของคุณเช่นนี้:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
หลังจากนั้นคุณสามารถยกเลิกด้วย:
scheduler.TaskThread.Abort();
โปรดทราบว่าข้อควรทราบเกี่ยวกับการยกเลิกเธรดยังคงมีผล:
Thread.Abort
วิธีควรใช้ด้วยความระมัดระวัง โดยเฉพาะอย่างยิ่งเมื่อคุณเรียกใช้เพื่อยกเลิกเธรดอื่นนอกเหนือจากเธรดปัจจุบันคุณไม่ทราบว่าโค้ดใดที่ได้ดำเนินการหรือล้มเหลวในการดำเนินการเมื่อThreadAbortExceptionถูกส่งออกไปและคุณไม่สามารถมั่นใจได้ว่าสถานะของแอปพลิเคชันของคุณ รับผิดชอบในการถนอมรักษา ตัวอย่างเช่นการโทรThread.Abort
อาจป้องกันการสร้างแบบคงที่จากการดำเนินการหรือป้องกันการปล่อยของทรัพยากรที่ไม่มีการจัดการ
Thread.Abort
คือไม่ได้รับการสนับสนุนบน. NET Core ความพยายามที่จะใช้มีผลให้เกิดข้อยกเว้น: System.PlatformNotSupportedException: การยกเลิกแทร็กไม่ได้รับการสนับสนุนบนแพลตฟอร์มนี้ ข้อแม้ที่สามคือSingleThreadTaskScheduler
ไม่สามารถใช้งานได้อย่างมีประสิทธิภาพกับงานตามสไตล์สัญญาหรือกล่าวอีกนัยหนึ่งกับงานที่สร้างขึ้นโดยasync
ผู้รับมอบสิทธิ์ ตัวอย่างเช่นการฝังตัวที่await Task.Delay(1000)
ทำงานในไม่มีเธรดดังนั้นจึงไม่ได้รับผลกระทบเป็นเหตุการณ์ของเธรด