C多线程
目录
C#多线程
启动方式
Task:基于Thread+ThreadPool结合
Task三种启动方式比较
【1】Task对象的Start方法启动,适合在符合条件情况下去启动,也就是可以按照需求启动,灵活控制。
【2】Run方法,是定义后直接启动。适合马上启动的情况。
【3】Task.Factory.StartNew,也是直接启动线程。(了解)
//【1】创建Task对象,然后调用Start方法启动线程
static void Test1()
{
Task task = new Task(() =>
{
Console.WriteLine("new 一个新的Task,对应的子线程ID=" + Thread.CurrentThread.ManagedThreadId);
});
task.Start();
}
//【2】使用Task的静态方法Run方法直接启动线程
static void Test2()
{
Task task = Task.Run(() =>
{
Console.WriteLine("使用Task中的Run方法,对应的子线程ID=" + Thread.CurrentThread.ManagedThreadId);
});
}
//【3】使用Factory启动
static void Test3()
{
Task task = Task.Factory.StartNew(() =>
{
Console.WriteLine("使用Task中的Factory.StartNew方法,对应的子线程ID=" + Thread.CurrentThread.ManagedThreadId);
});
}
线程启动取消暂停继续
取消再启动会重新从1开始
cts、mRestEvent是被多个线程共享的
Register是回调方法(执行完自动执行某个方法),每个线程结束都会执行一次
//取消任务信号源对象,是被多个线程共享的
private CancellationTokenSource cts = new CancellationTokenSource();
//手动停止事件对象,放在线程执行过程中,true,不会被阻塞
private ManualResetEvent mRestEvent = new ManualResetEvent(true);
private void button1_Click(object sender, EventArgs e)
{
if (cts.IsCancellationRequested)
cts = new CancellationTokenSource();
Task task = Task.Run(() =>
{
int cnt = 0;
while (!cts.IsCancellationRequested)
{
//用来控制是否需要暂停和继续
mRestEvent.WaitOne();
cnt++;
Thread.Sleep(300);
Console.WriteLine(cnt);
}
},cts.Token);
cts.CancelAfter(3000);//3s后自动取消
//只要线程取消后就会执行
cts.Token.Register(() => { Console.WriteLine("任务取消,开始清理工作"); });
}
private void button2_Click(object sender, EventArgs e)
{
cts.Cancel();
}
private void button3_Click(object sender, EventArgs e)
{
mRestEvent.Reset();//将事件状态设置为未发送信号(false)被阻塞
}
private void button4_Click(object sender, EventArgs e)
{
mRestEvent.Set();//将事件状态设置为已发送信号(true)
}
线程等待
//等待所有线程完成
Task.WaitAll(task1, task2);
//只需等待一个线程完成
Task.WaitAny(task1, task2);
Console.WriteLine("主线程开始运行");
线程延续
Task.WhenAll(task1, task2).ContinueWith() //都执行完就执行第三个线程
Task.WhenAny(task1, task2).ContinueWith() //只要有一个执行完就执行第三个线程
private void button1_Click(object sender, EventArgs e)
{
if (cts.IsCancellationRequested)
cts = new CancellationTokenSource();
Task task1 = Task.Run(() =>
{
Console.WriteLine("task1");
}, cts.Token);
Task task2 = Task.Run(() =>
{
Console.WriteLine("task2");
}, cts.Token);
Task.WhenAll(task1, task2).ContinueWith(x =>
{
Console.WriteLine("task3");
});
Console.WriteLine("主线程开始运行");
}
跨线程访问卡顿
主线程创建的控件,是不能直接被子线程访问的,要使用
task.Start(TaskScheduler.FromCurrentSynchronizationContext());
将耗时任务和其他任务分开就不会卡顿了,可以点击其他按钮
public Main()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Task task = new Task(() =>
{
//Thread.Sleep(5000);模拟耗时任务,会卡,不能做其他事
this.label1.Text = "abcd";
});
task.Start(TaskScheduler.FromCurrentSynchronizationContext());
}
private void button3_Click(object sender, EventArgs e)
{
Task task = Task.Run(() =>
{
Thread.Sleep(5000);
});
//延续任务,耗时任务后执行,可以做其他事
task.ContinueWith(t =>
{
this.label1.Text = "abcd123";
}, TaskScheduler.FromCurrentSynchronizationContext());
}
LongRunning运行长时间的任务
Task.Run()要用Func带返回值委托
private void button1_Click(object sender, EventArgs e)
{
Task task1 = new Task(() =>
{
Thread.Sleep(2000);
Console.WriteLine("111");
},TaskCreationOptions.LongRunning);
task1.Start();
}
锁
对某一个资源(对象)加锁,防止线程争夺,其他线程必须等待
private int nums = 0;
private object myLock = new object();//可以不用定义,除非是静态方法
public void TestCount()
{
//lock (myLock)
//{
// for (int i = 0; i < 100; i++)
// {
// nums++;
// Console.WriteLine(nums);
// }
//}
//
lock (this) //只能锁住线程能访问到的对象
{
for (int i = 0; i < 100; i++)
{
nums++;
Console.WriteLine(nums);
}
}
}
public void Start()
{
for (int i = 0; i < 5; i++)
{
Task.Run(() =>
{
TestCount();
});
}
}
aysnc/await
配合使用
class Program
{
static void Main(string[] args)
{
string str = GetText().Result;
Console.WriteLine(str);
}
async static Task<string> GetText()
{
FileStream fs = new FileStream("mytxt.txt", FileMode.Open);
byte[] filebytes = new byte[fs.Length];
await fs.ReadAsync(filebytes, 0, filebytes.Length);
return Encoding.UTF8.GetString(filebytes);
}
}