温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C#多线程Task的使用

发布时间:2021-08-25 17:11:15 来源:亿速云 阅读:299 作者:chen 栏目:大数据

这篇文章主要讲解了“C#多线程Task的使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C#多线程Task的使用”吧!

背景

当我们程序在执行长时间数据处理时,如果只用主进程,会出现卡死或是假死等待时间长的情况,如果这个时间我们用到多线程操作,不但可以减少耗时,执行过程中可以简单同步UI界面,让人看到不会有种假死的状态.

要求

我们要遍达30W数据,分成6个任务,每个任务处理5W数据,然后同时并行的任务是3项.每个任务遍历完成后生成一个TXT的文件.

设计思路

我们通过多个任务添加多个用户控件的方法,然后在控件的TAG里设置为bool类型,用于判断任务是否已经执行了,然后在线程操作的时候首先找到未执行的任务,然后更新TAG标志并处理任务,处理完当前任务数据后再把数据写入到当前目录下的txt文件里面,直到所有的任务都完成后结束线程.

知识点

  • 用户控件的设计

  • 动态创建控件

  • 线程Task的操作

编码

打开VS,我用的是2017,新建一个项目名称为TaskDemo

在Form里面我们加三个控件,分别是FlowLayoutPanel ,TextBox和一个Button

C#多线程Task的使用

左边的FlowLayoutPanel是一会儿我们自己建个用户控件来显示任务列表的,这个可以自适应排列.

接下来我们在解决方案里面鼠标右键选择添加新建一个用户控件起名为TaskCtrl

C#多线程Task的使用

C#多线程Task的使用

建好后我们在这里面增加几个Label和一个ProgressBar

C#多线程Task的使用

在userctrl里定义三个值

        //任务号
        public int Taskno;
        //开始记录数
        public int Startrecord;
        //结束记录数
        public int Endrecord;

然后重新改一下构造函数

        public TaskCtrl(int taskno, int startrecord, int endrecord)
        {
            Taskno = taskno;
            Startrecord = startrecord;
            Endrecord = endrecord;
            InitializeComponent();

            lblTaskNo.Text = "任务号:" + Taskno;
            lblRecord.Text = "任务记录数:" + (Endrecord - Startrecord);

            prgb.Minimum = Startrecord;
            prgb.Maximum = Endrecord;
            prgb.Value = Startrecord;
        }

我们再增加一个方法,用于更新显示当前的任务

        /// <summary>
        /// 显示进度条和信息
        /// </summary>
        /// <param name="row"></param>
        public void ShowProgressbar(int row)
        {
            prgb.Value = row;
            lblStatus.Text = row + "/" + Endrecord;
        }

这样用户控件我们就完成了.下面开始写主界面的按钮事件

首先我们定义几个值,像总数据,每项任务的值,同时并行任务数

C#多线程Task的使用

然后我们再定义一个委托,用于线程执行过程中可以同步UI显示当前状态的

C#多线程Task的使用

接下来就是我们开始写按钮事件

先写一个初始化用户控件的方法

        /// <summary>
        /// 初始化flowlayoutpanel获取用户控件
        /// </summary>
        private void InitFlowLayoutPanel()
        {
            flpnl.Controls.Clear();
            //计算任务个数
            //总数除每个任务最大数,如果有余数任务数+1,如果不有余数就是任务数
            int taskqty = totalrecord % taskrecord > 0 ? totalrecord / taskrecord + 1 : taskrecord / taskrecord;
            //循环自动创建用户控件
            for (int i = 0; i < taskqty; i++)
            {
                //计算当前任务的开始记录和结束记录
                int startrecord = i == 0 ? 1 : i * taskrecord + 1;
                int endrecord = i == 0 ? taskrecord : (i + 1) * taskrecord;

                TaskCtrl userctrl = new TaskCtrl(i, startrecord, endrecord);
                userctrl.Name = "Task" + i;
                //Tag标志用于记录当前任务是否已经完成
                userctrl.Tag = false;

                flpnl.Controls.Add(userctrl);
            }
        }

业务处理的核心代码

       /// <summary>
        /// 线程操作
        /// </summary>
        private void TaskModify()
        {
            Task parent = new Task(() =>
            {
                var cts = new CancellationTokenSource();
                var tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent,
                    TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);

                //定义数组执行的任务
                Task[] childTasks = new Task[tasknum - 1];
                //赋值
                for (int task = 0; task < tasknum; task++)
                {
                    childTasks [task] = tf.StartNew(() =>
                    {
                        bool res = true;
                        while (res)
                        {
                            Thread.Sleep(2000);
                            //每次执行完后得到返回值用于查询是否还有未完成任务
                            //如果有继续执行,没有就退出线程
                            res = DoTask();
                        }
                    }, cts.Token).ContinueWith(t =>
                    {
                        if (t.IsCompleted)
                        {
                            this.BeginInvoke(TextShow, "当前线程任务都执行完毕!!");
                        }
                        else if (t.IsCanceled)
                        {
                            this.BeginInvoke(TextShow, "当前线程取消任务!!");
                        }
                        else if (t.IsFaulted)
                        {
                            this.BeginInvoke(TextShow, "当前线程异常!!");
                        }
                    }, cts.Token);
                    Thread.Sleep(200);
                }
            });

            parent.Start();
        }

数据处理的相关代码

       /// <summary>
        /// 获取用户控件,判断是否已经执行完任务
        /// </summary>
        /// <returns></returns>
        private TaskCtrl GetUserCtrl()
        {
            TaskCtrl ctrl = null;
            foreach (Control c in flpnl.Controls)
            {
                ctrl = c as TaskCtrl;
                //判断用户控件是否已经完成任务
                if (ctrl != null && !(bool) ctrl.Tag)
                {
                    return ctrl;
                }
            }
            return null;
        }

        private bool DoTask()
        {
            bool res = false;
            string str = string.Empty;
            //获取用户控件,如果不为空即开始任务
            TaskCtrl taskCtrl = GetUserCtrl();
            if (taskCtrl != null)
            {
                //获取后更新用户控件标志
                res = true;
                taskCtrl.Tag = res;
                //定义上次显示的行数
                int lastshow = 0;
                for (int row = taskCtrl.Startrecord; row < taskCtrl.Endrecord; row++)
                {
                    //拼接循环的字符串
                    str = str + "<record>" + row + "</record>";
                    //防止每条都更新UI造成阻塞,设置每1000条更新一次UI
                    if (row - lastshow > 1000 || row == taskCtrl.Endrecord)
                    {
                        this.BeginInvoke(TaskProcessbar, taskCtrl, row);
                        lastshow = row;
                    }
                }
                this.BeginInvoke(TaskProcessbar, taskCtrl, taskCtrl.Endrecord);
                //将循环完后拼接好的字符串保存到txt文件里,文件名为用户控件名称
                string filePath = Directory.GetCurrentDirectory() + "\\file" + taskCtrl.Name + ".txt";
                System.IO.File.WriteAllText(filePath, str, Encoding.UTF8);               
            }
            return res;
        }

接下来我们运行看看效果

C#多线程Task的使用

C#多线程Task的使用

C#多线程Task的使用

任务执行完后程序目录下生成对应的txt文件

C#多线程Task的使用

感谢各位的阅读,以上就是“C#多线程Task的使用”的内容了,经过本文的学习后,相信大家对C#多线程Task的使用这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI