c# 线程使用总结

C# 里有好几种线程,每种有不同的实现方式,对应实现不同的功能,做个总结方便以后使用。

 

1. 委托

(1) 同步委托开启线程

不需要传参:

Action action1 = new Action(Count);// count 是一个计数函数
action1.Invoke();

 

需要传参:

Action<string> action2 = new Action<string>(Count);// Count是一个带有object参数的计数函数
action2.Invoke("线程开启");

总结:同步委托可以实现传参与不传参两种情况,但使用以后,界面卡顿,大多数情况下不宜使用

 

(2)异步委托开启线程

传递参数:

Action<string> action3 = new Action<string>(Count);// Count是一个带有object参数的计数函数
action3.BeginInvoke("线程开启", null, null);

 传递参数并等待线程结束,传回线程运行之后的返回值:

private delegate int CallBack(string value);//带有返回值
int count = 0;
private int Back(string str)
{
   Stopwatch sp = new Stopwatch();
   sp.Start();
   for (int i = 10; i > 0; i--)
   {
        System.TimeSpan startTime = sp.Elapsed;
         while ((sp.Elapsed - startTime).TotalMilliseconds <= 1000)
         {
               System.Threading.Thread.Sleep(1);
         }
         TextChange(textBox1, str);
         TextChange(textBox2, i.ToString());
         count++;
    }
    return count;
}


CallBack action = new CallBack(Back);
IAsyncResult cookie = action.BeginInvoke("线程开启", null, null);
int return= action.EndInvoke(cookie);

 

总结:如果只传递参数,异步线程不会卡顿,但如果需要等待线程结束 ,用到 action.EndInvoke,界面会卡顿,

这时外加上一个new Thread或者其他线程,可以解决卡顿问题,这个后面会说到。

 

(3) 通过thread开启线程

传递参数:

Thread thread = new Thread(new ParameterizedThreadStart(Count));// Count是一个带有object参数的计数函数
thread.Start("线程开启");
//Thread.Sleep(1000);
//thread.Abort();//线程终止,立马停下,方法停止
//Console.WriteLine(thread.ThreadState);

       或者用一个更简单的方式:

new Thread((value)=>
{
     //这里直接写Count函数的内容
}).Start("线程开启");

不传递参数:

Thread thread = new Thread(new ThreadStart(Count));// Count是不带参数的计数函数
thread.Start();
//Thread.Sleep(1000);
//thread.Abort();//线程终止,立马停下,方法停止
//Console.WriteLine(thread.ThreadState);

      或者(这里演示用new thread解决action.EndInvoke卡顿问题):

new Thread(()=>
{
CallBack action = new CallBack(Back);
IAsyncResult cookie = action.BeginInvoke("线程开启", null, null);
int return= action.EndInvoke(cookie);

int count = 0;
private int Back(string str)
{
   Stopwatch sp = new Stopwatch();
   sp.Start();
   for (int i = 10; i > 0; i--)
   {
        System.TimeSpan startTime = sp.Elapsed;
         while ((sp.Elapsed - startTime).TotalMilliseconds <= 1000)
         {
               System.Threading.Thread.Sleep(1);
         }
         TextChange(textBox1, str);
         TextChange(textBox2, i.ToString());
         count++;
    }
    return count;
}

}).Start();

 

(4)  timer开启线程

可以传递参数并循环执行:

System.Threading.Timer timer = new System.Threading.Timer(new TimerCallback(Count), "线程开启", Timeout.Infinite, Timeout.Infinite);// Count是一个带有object参数的计数函数
timer.Change(0, 0); //开启timer
//Thread.Sleep(100); 
//timer.Change(-1, -1);//关闭timer,如果count函数没有执行完,不会停下,直到count函数执行完毕

总结:如果不需要传参,把“线程开启”换成null即可。

timer.Change(0,0) 中第一个0的位置表示开启时间(单位ms),0表示立马执行,-1表示永远不执行,第二个0的位置表示循环周期(单位ms),0或者-1表示不循环,其他正整数表示循环周期。

 

(5) Task开启线程

不传递参数(快速启动方式):

Task.Run(() => Count());//Count是一个计时函数

传递参数:

Task task = new Task(new Action<object>(Count), "线程开启", cts.Token);//无返回值的传参
task.Start();

//或者
Task.Factory.StartNew(new Action<object>(Count), "线程开启");

等待单个线程结束并返回结果:

int count = 0;
private int Test2(object obj)
{
   Stopwatch sp = new Stopwatch();
   sp.Start();
   for (int i = 5; i > 0; i--)
   {
        System.TimeSpan startTime = sp.Elapsed;
        while ((sp.Elapsed - startTime).TotalMilliseconds <= 1000)
         {
                 System.Threading.Thread.Sleep(1);
         }
           TextChange(textBox2, i.ToString());
           TextChange(textBox1, obj.ToString());
           count++;
   }
     return count;
}



Func<object, int> func = new Func<object, int>(Test2);            Task<int> task1 = new Task<int>(func, "线程开启");
task1.Start();

//if(task1.Result ==5) //直接写等待并判断会卡界面
//{
//    TextChange(textBox6, task1.Result.ToString());
//}


//开启另一个task2等待task1结束
Task task2 = task1.ContinueWith(reValue =>
{
    if (reValue.Result == 5)
    {
         TextChange(textBox6, reValue.Result.ToString());
     }
     else
     {
          return;
      }
});

等待多个线程全部运行结束,并返回结果 :

int count = 0;
private int Test2(object obj)
{
   Stopwatch sp = new Stopwatch();
   sp.Start();
   for (int i = 5; i > 0; i--)
   {
        System.TimeSpan startTime = sp.Elapsed;
        while ((sp.Elapsed - startTime).TotalMilliseconds <= 1000)
         {
                 System.Threading.Thread.Sleep(1);
         }
           TextChange(textBox2, i.ToString());
           TextChange(textBox1, obj.ToString());
           count++;
   }
     return count;
}


List<int> resulesList = new List<int>();
List<Task<int>> taskList = new List<Task<int>>();
for (int i = 0; i < 5; i++)
{
     taskList.Add(Task<int>.Factory.StartNew(new Func<object, int>(test2), i));
}
Task.Factory.ContinueWhenAll(taskList.ToArray(), t =>       //通过回调完成主线程任务,ContinueWhenAll创建一组任务,该任务在指定的一组任务完成后开始
{
     for (int i = 0; i < 5; i++)
     {
         resulesList.Add(taskList[i].Result);
      }
});

等待多个线程任一运行结束,并返回结果 :

Task.Factory.ContinueWhenAny

 总结:Task线程的建立在一定程度上依赖于委托,通常,不需要返回值用Action,需要返回值用Func,也可以自建委托,

Task可以等待单一线程结束/多个线程结束/任一线程结束,分别对应不同的方法

 

(6) async / await 异步等待

没有返回值的等待:

private async void Waiting()
{
   var t = Task.Run(() =>
   {
        Thread.Sleep(5000);
    });
    await t;
Console.WriteLine("等待结束"); }

 

等待返回值:

private async void Waiting()
{
   var t = Task.Run(() =>
   {
        Thread.Sleep(5000);
        return "线程结束";
    });
    textBox2.Text = await t;        
}

总结:这种方式可以等待结果,界面不卡顿,且跨线程修改控件属性问题也可以避免

 

posted @ 2022-03-07 18:57  寻找迷途的羔羊  阅读(654)  评论(0)    收藏  举报