吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1394|回复: 13
收起左侧

[已解决] C# 异步读取文件任务,一次取消不了任务,需要多次才能取消任务

[复制链接]
Cool_Breeze 发表于 2021-5-6 18:55
本帖最后由 Cool_Breeze 于 2021-5-24 11:30 编辑

请务必将任务理解为工作的异步抽象,而非 在线程之上的抽象。 默认情况下,任务在当前线程上执行,且在适当时会将工作委托给操作系统。

解决了。异步执行时,主线程会收到终止异常。所以需要在主线程中终止任务后,在开始新的任务!
[C#] 纯文本查看 复制代码
using System;
using System.Drawing;
using System.Threading;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;

public class Form1 : Form
{
    [STAThread]
    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[4194304];
        byte[] content = new byte[1024];
        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;
    }}
1.png

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

wwh1004 发表于 2021-5-6 20:41
using System;
using System.Drawing;
using System.Threading;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;

public class Form1 : Form {
    [STAThread]
    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[10240];
        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;
    }
}

免费评分

参与人数 1吾爱币 +2 热心值 +1 收起 理由
Cool_Breeze + 2 + 1 感谢大佬解惑!

查看全部评分

wwh1004 发表于 2021-5-6 20:02

说实话,代码问题挺多的,全修改完挺麻烦的,目前就把看见的逻辑错误改正确了。

using System;
using System.Drawing;
using System.Threading;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;

public class Form1 : Form {
    [STAThread]
    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[100];
        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;
    }
}

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
苏紫方璇 + 1 + 1 用心讨论,共获提升!

查看全部评分

wwh1004 发表于 2021-5-6 20:06
本帖最后由 wwh1004 于 2021-5-6 20:09 编辑

那个targetEnd相关的,没太看懂是干嘛的....如果任务已经完成了,那不需要手动取消的,可以不需要传那个cancellationtoken,也不需要targetEnd

再补充个下,那个分块读取的缓冲区设置的太小了,vs调试器看着clr在疯狂进行gc,按道理给个几mb都没问题。如果只是要文件完整内容,一次性全部读出来也可以,.net内部已经设计好缓冲区了,不需要自己再弄个。
 楼主| Cool_Breeze 发表于 2021-5-6 20:08
wwh1004 发表于 2021-5-6 20:02
[md]说实话,代码问题挺多的,全修改完挺麻烦的,目前就把看见的逻辑错误改正确了。

``` cs

感谢大佬指点,但是问题还是存在,打开大文件,中途还是无法取消!
wwh1004 发表于 2021-5-6 20:10
Cool_Breeze 发表于 2021-5-6 20:08
感谢大佬指点,但是问题还是存在,打开大文件,中途还是无法取消!

取消就是从头开始读取了,你是要暂停还是取消?
 楼主| Cool_Breeze 发表于 2021-5-6 20:13
wwh1004 发表于 2021-5-6 20:10
取消就是从头开始读取了,你是要暂停还是取消?

就是打开大文件后,还在读取中,又重新选择一个小文件打开,小文件跑完,大文件还在跑。逻辑是:打开小文件时,读取大文件任务就该是取消了!
 楼主| Cool_Breeze 发表于 2021-5-6 20:16
wwh1004 发表于 2021-5-6 20:06
那个targetEnd相关的,没太看懂是干嘛的....如果任务已经完成了,那不需要手动取消的,可以不需要传那个can ...

看到 有个 ReadAsync 重载是没有 token的。targetEnd其实就是检测读取文件任务是否完成!
 楼主| Cool_Breeze 发表于 2021-5-6 20:17
wwh1004 发表于 2021-5-6 20:06
那个targetEnd相关的,没太看懂是干嘛的....如果任务已经完成了,那不需要手动取消的,可以不需要传那个can ...

感谢指正! 我就说读取个文件占用cpu那么多资源!
 楼主| Cool_Breeze 发表于 2021-5-6 20:46
wwh1004 发表于 2021-5-6 20:41
[md]``` cs
using System;
using System.Drawing;

成功了。大佬能不能教教 () => stopped 这个委托它做了啥?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-26 02:26

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表