C# 异步读取文件任务,一次取消不了任务,需要多次才能取消任务
本帖最后由 Cool_Breeze 于 2021-5-24 11:30 编辑请务必将任务理解为工作的异步抽象,而非 在线程之上的抽象。 默认情况下,任务在当前线程上执行,且在适当时会将工作委托给操作系统。
解决了。异步执行时,主线程会收到终止异常。所以需要在主线程中终止任务后,在开始新的任务!using System;
using System.Drawing;
using System.Threading;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
public class Form1 : Form
{
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
private readonly ProgressBar pbar;
private readonly Label label;
private readonly Button StartPausebut;
private readonly Button openFileBut;
private readonly Button Stopbut;
private readonly Button ShowHandle;
private readonly ListBox fileList;
private static CancellationTokenSource source = new CancellationTokenSource();
private readonly EventWaitHandle StartPausebutEventsynchro = new EventWaitHandle(false, EventResetMode.ManualReset);
private string selectFileName;
private string oldSelectFileName;
private Task task;
// 窗体
public Form1()
{
// 进度条
pbar = new ProgressBar();
pbar.Width = 500;
pbar.Location = new Point(10, 20);
pbar.Visible = true;
// 开始,暂停按键
StartPausebut = new Button();
StartPausebut.Text = "开始";
StartPausebut.Location = new Point(100, 60);
StartPausebut.Click += new EventHandler(StartPausebut_Click);
// 上一个
Button selectUP = new Button();
selectUP.Text = "上一个";
selectUP.Location = new Point(10, 90);
selectUP.Click += new EventHandler(selectUP_Click);
// 下一个
Button selectDown = new Button();
selectDown.Text = "下一个";
selectDown.Location = new Point(100, 90);
selectDown.Click += new EventHandler(selectDown_Click);
// 取消按键
Stopbut = new Button();
Stopbut.Text = "终止读取";
Stopbut.Location = new Point(190, 60);
Stopbut.Click += new EventHandler(Stopbut_Click);
// 显示窗口句柄
ShowHandle = new Button();
ShowHandle.Text = "显示窗口句柄";
ShowHandle.Location = new Point(280, 60);
ShowHandle.Click += ShowHandle_Click;
// 选择文件状态
openFileBut = new Button();
openFileBut.Text = "选择文件";
openFileBut.Location = new Point(10, 60);
openFileBut.Click += new EventHandler(openFileBut_Click);
// 加载文件列表
Button loadBut = new Button();
loadBut.Text = "加载文件列表";
loadBut.Width = 100;
loadBut.Location = new Point(370, 60);
loadBut.Click += new EventHandler(loadBut_Click);
// 提示信息标签
label = new Label();
label.Location = new Point(10, 120);
label.AutoSize = true;
//文件列表内容
fileList = new ListBox();
fileList.Size = new System.Drawing.Size(500,400);
fileList.Location = new Point(10, 140);
fileList.MultiColumn = true;
this.Controls.Add(StartPausebut);
this.Controls.Add(pbar);
this.Controls.Add(label);
this.Controls.Add(Stopbut);
this.Controls.Add(openFileBut);
this.Controls.Add(ShowHandle);
this.Controls.Add(fileList);
this.Controls.Add(loadBut);
this.Controls.Add(selectUP);
this.Controls.Add(selectDown);
this.Width = 600;
this.Height = 600;
}
// 开始,暂停 按键事件
private void StartPausebut_Click(object sender, EventArgs e)
{
Application.DoEvents();
if (StartPausebut.Text == "开始")
{
StartPausebut.Text = "暂停";
StartPausebutEventsynchro.Set(); // 任务非阻塞状态
if (task == null || task.IsCompleted)
{
oldSelectFileName = selectFileName;
task = Task.Run(() => openFileStream());
}
else if (selectFileName != oldSelectFileName)
{
Stopbut_Click(this, new EventArgs());
task = Task.Run(() => openFileStream());
StartPausebut.Text = "暂停";
}
return ;
}
else if (StartPausebut.Text == "暂停")
{
StartPausebut.Text = "开始";
StartPausebutEventsynchro.Reset(); // 任务阻塞状态
return ;
}
}
// 选择文件
private void openFileBut_Click(object sender, EventArgs e)
{
// 选择文件对话框
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "Text documents (*.*)|*.*";
dlg.ShowDialog();
selectFileName = dlg.FileName;
label.Text = selectFileName;
if (selectFileName == "")
{
return;
}
}
// 取消任务
private void Stopbut_Click(object sender, EventArgs e)
{
// if (!source.Token.IsCancellationRequested)
try
{
if (task.Status != TaskStatus.RanToCompletion)
{
source.Cancel(); // 取消任务
StartPausebutEventsynchro.Reset();
Application.DoEvents();
StartPausebutEventsynchro.Set();
}
else
{
return;
}
}
catch {return;}
try
{
task.Wait();
}
catch
{
StartPausebut.Text = "开始";
source.Dispose();
source = new CancellationTokenSource(); //重新获取取消任务对象
}
}
// 显示窗口句柄
private void ShowHandle_Click(object sender, EventArgs e)
{
label.Text = Handle.ToString("X");
}
// 文件内容读取(每次读取 4MB 内容)
private async Task openFileStream()
{
pbar.Value = 0;
// byte[] content = new byte;
byte[] content = new byte;
int Count = 0;
using (FileStream fr = new FileStream(selectFileName, FileMode.Open))
{
pbar.Maximum = (int)fr.Length; // 文件过大这里会内存溢出
int total = 0;
while (true)
{
// 阻塞线程事件
// await Task.Run(() => StartPausebutEventsynchro.WaitOne());
StartPausebutEventsynchro.WaitOne();
source.Token.ThrowIfCancellationRequested();
// 异步读取 100 字节 文件内容
total = await fr.ReadAsync(content, 0, content.Length, source.Token);
if (total == 0)
{
break;
}
// 进度条
Count += total;
label.Text = Count.ToString() + " 字节";
pbar.Step = total;
pbar.PerformStep();
Application.DoEvents();
}
}
StartPausebut.Text = "开始";
}
// 加载文件事件
private void loadBut_Click(object sender, EventArgs e)
{
if (selectFileName == "" || selectFileName == null) return;
fileList.BeginUpdate();
fileList.Items.Clear();
string filePath = Path.GetDirectoryName(selectFileName);
foreach (var n in Directory.GetFiles(filePath))
{
fileList.Items.Add(n);
}
fileList.EndUpdate();
}
// 文件列表选中条上移
private void selectUP_Click(object sender, EventArgs e)
{
if (fileList.Items.Count == 0) return;
int count = fileList.Items.Count;
int current = fileList.SelectedIndex;
current--;
if (current < 0)
{
current = count - 1;
}
fileList.SelectedIndex = current;
label.Text = fileList.SelectedItem.ToString();
selectFileName = label.Text;
}
// 文件列表选中条下移
private void selectDown_Click(object sender, EventArgs e)
{
if (fileList.Items.Count == 0) return;
int count = fileList.Items.Count;
int current = fileList.SelectedIndex;
current++;
if (current >= count)
{
current = 0;
}
fileList.SelectedIndex = current;
label.Text = fileList.SelectedItem.ToString();
selectFileName = label.Text;
}} ``` cs
using System;
using System.Drawing;
using System.Threading;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
public class Form1 : Form {
public static void Main() {
Application.EnableVisualStyles();
Application.Run(new Form1());
}
private ProgressBar pbar;
private Label label;
private Button StartPausebut;
private Button openFileBut;
private static CancellationTokenSource source = new CancellationTokenSource();
private bool paused;
private bool stopped;
private System.Threading.SemaphoreSlim StartPausebutEventsynchro = new SemaphoreSlim(0);
private string selectFileName;
private bool targetEnd = false;
// 窗体
public Form1() {
// 进度条
pbar = new ProgressBar();
pbar.Width = 500;
pbar.Location = new Point(10, 20);
pbar.Visible = true;
// 开始,暂停按键
StartPausebut = new Button();
StartPausebut.Text = "开始";
StartPausebut.Location = new Point(100, 60);
StartPausebut.Click += new EventHandler(StartPausebut_Click);
// 选择文件状态
openFileBut = new Button();
openFileBut.Text = "选择文件";
openFileBut.Location = new Point(10, 60);
openFileBut.Click += new EventHandler(openFileBut_Click);
// 提示信息标签
label = new Label();
label.Location = new Point(10, 90);
label.AutoSize = true;
this.Controls.Add(StartPausebut);
this.Controls.Add(pbar);
this.Controls.Add(label);
this.Controls.Add(openFileBut);
this.Width = 600;
}
// 开始,暂停 按键事件
private void StartPausebut_Click(object sender, EventArgs e) {
if (StartPausebut.Text == "开始") {
StartPausebut.Text = "暂停";
paused = true; // 任务非阻塞状态
}
else if (StartPausebut.Text == "暂停") {
StartPausebut.Text = "开始";
paused = false;
StartPausebutEventsynchro.Release(); // 任务阻塞状态
}
}
// 选择文件
private async void openFileBut_Click(object sender, EventArgs e) {
// 选择文件对话框
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "Text documents (*.*)|*.*";
dlg.ShowDialog();
selectFileName = dlg.FileName;
label.Text = selectFileName;
if (selectFileName == "") {
return;
}
try {
// 每次选择文件都会取消之前的任务!
// 取消任务
if (targetEnd) {
source.Cancel(); // 取消任务
await Task.Run(() => SpinWait.SpinUntil(() => stopped));
targetEnd = false;
}
await openFileStream(); // 捕获取消任务后引发的异常
}
catch (Exception err) {
label.Text = err.Message;
}
finally {
source.Dispose(); // 释放资源
source = new CancellationTokenSource(); //重新获取取消任务对象
stopped = true;
}
}
// 文件内容读取(每次读取 100 字节 内容)
private async Task openFileStream() {
targetEnd = true; // 标记读取文件状态(正在读取,完成读取)
pbar.Value = 0;
byte[] content = new byte;
int Count = 0;
using (FileStream fr = new FileStream(selectFileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
pbar.Maximum = (int)fr.Length;
int total = 0;
while (true) {
// 阻塞线程事件
if (paused)
await StartPausebutEventsynchro.WaitAsync(source.Token);
// 异步读取 100 字节 文件内容
total = await fr.ReadAsync(content, 0, content.Length, source.Token);
if (total == 0) {
break;
}
// 进度条
Count += total;
label.Text = Count.ToString() + " 字节";
pbar.Step = total;
pbar.PerformStep();
}
}
targetEnd = false;
}
}
``` 说实话,代码问题挺多的,全修改完挺麻烦的,目前就把看见的逻辑错误改正确了。
``` cs
using System;
using System.Drawing;
using System.Threading;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
public class Form1 : Form {
public static void Main() {
Application.EnableVisualStyles();
Application.Run(new Form1());
}
private ProgressBar pbar;
private Label label;
private Button StartPausebut;
private Button openFileBut;
private static CancellationTokenSource source = new CancellationTokenSource();
private bool paused;
private System.Threading.SemaphoreSlim StartPausebutEventsynchro = new SemaphoreSlim(0);
private string selectFileName;
private bool targetEnd = false;
// 窗体
public Form1() {
// 进度条
pbar = new ProgressBar();
pbar.Width = 500;
pbar.Location = new Point(10, 20);
pbar.Visible = true;
// 开始,暂停按键
StartPausebut = new Button();
StartPausebut.Text = "开始";
StartPausebut.Location = new Point(100, 60);
StartPausebut.Click += new EventHandler(StartPausebut_Click);
// 选择文件状态
openFileBut = new Button();
openFileBut.Text = "选择文件";
openFileBut.Location = new Point(10, 60);
openFileBut.Click += new EventHandler(openFileBut_Click);
// 提示信息标签
label = new Label();
label.Location = new Point(10, 90);
label.AutoSize = true;
this.Controls.Add(StartPausebut);
this.Controls.Add(pbar);
this.Controls.Add(label);
this.Controls.Add(openFileBut);
this.Width = 600;
}
// 开始,暂停 按键事件
private void StartPausebut_Click(object sender, EventArgs e) {
if (StartPausebut.Text == "开始") {
StartPausebut.Text = "暂停";
paused = true; // 任务非阻塞状态
}
else if (StartPausebut.Text == "暂停") {
StartPausebut.Text = "开始";
paused = false;
StartPausebutEventsynchro.Release(); // 任务阻塞状态
}
}
// 选择文件
private async void openFileBut_Click(object sender, EventArgs e) {
// 选择文件对话框
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "Text documents (*.*)|*.*";
dlg.ShowDialog();
selectFileName = dlg.FileName;
label.Text = selectFileName;
if (selectFileName == "") {
return;
}
try {
// 每次选择文件都会取消之前的任务!
// 取消任务
if (targetEnd) {
source.Cancel(); // 取消任务
targetEnd = false;
}
await openFileStream(); // 捕获取消任务后引发的异常
}
catch (Exception err) {
label.Text = err.Message;
}
finally {
source.Dispose(); // 释放资源
source = new CancellationTokenSource(); //重新获取取消任务对象
}
}
// 文件内容读取(每次读取 100 字节 内容)
private async Task openFileStream() {
targetEnd = true; // 标记读取文件状态(正在读取,完成读取)
pbar.Value = 0;
byte[] content = new byte;
int Count = 0;
using (FileStream fr = new FileStream(selectFileName, FileMode.Open)) {
pbar.Maximum = (int)fr.Length;
int total = 0;
while (true) {
// 阻塞线程事件
if (paused)
await StartPausebutEventsynchro.WaitAsync();
// 异步读取 100 字节 文件内容
total = await fr.ReadAsync(content, 0, content.Length, source.Token);
if (total == 0) {
break;
}
// 进度条
Count += total;
label.Text = Count.ToString() + " 字节";
pbar.Step = total;
pbar.PerformStep();
Application.DoEvents();
}
}
targetEnd = false;
}
}
``` 本帖最后由 wwh1004 于 2021-5-6 20:09 编辑
那个targetEnd相关的,没太看懂是干嘛的....如果任务已经完成了,那不需要手动取消的,可以不需要传那个cancellationtoken,也不需要targetEnd
再补充个下,那个分块读取的缓冲区设置的太小了,vs调试器看着clr在疯狂进行gc,按道理给个几mb都没问题。如果只是要文件完整内容,一次性全部读出来也可以,.net内部已经设计好缓冲区了,不需要自己再弄个。 wwh1004 发表于 2021-5-6 20:02
说实话,代码问题挺多的,全修改完挺麻烦的,目前就把看见的逻辑错误改正确了。
``` cs
感谢大佬指点,但是问题还是存在,打开大文件,中途还是无法取消! Cool_Breeze 发表于 2021-5-6 20:08
感谢大佬指点,但是问题还是存在,打开大文件,中途还是无法取消!
取消就是从头开始读取了,你是要暂停还是取消? wwh1004 发表于 2021-5-6 20:10
取消就是从头开始读取了,你是要暂停还是取消?
就是打开大文件后,还在读取中,又重新选择一个小文件打开,小文件跑完,大文件还在跑。逻辑是:打开小文件时,读取大文件任务就该是取消了! wwh1004 发表于 2021-5-6 20:06
那个targetEnd相关的,没太看懂是干嘛的....如果任务已经完成了,那不需要手动取消的,可以不需要传那个can ...
看到 有个 ReadAsync 重载是没有 token的。targetEnd其实就是检测读取文件任务是否完成! wwh1004 发表于 2021-5-6 20:06
那个targetEnd相关的,没太看懂是干嘛的....如果任务已经完成了,那不需要手动取消的,可以不需要传那个can ...
感谢指正! 我就说读取个文件占用cpu那么多资源! wwh1004 发表于 2021-5-6 20:41
``` cs
using System;
using System.Drawing;
成功了。大佬能不能教教 () => stopped 这个委托它做了啥?
页:
[1]
2