直接代码
主线程的输出“终于轮到老子了”与子线程的输出会随机穿插出现。但是我需要让主线程输出在最后,这如何实现呢?这里考虑到Thread.Join,会阻塞调用线程,直到子线程完成位置。那么在每个子线程都调用Join方法会如何?修改代码如下:
这里每次执行主线程的输出每次都会在子线程完成之后输出,但是子线程调用Join方法后,不仅仅阻止了主线程的调用,也阻止了其他子线程的调用,故此此处加了Join方法之后是相当于单线程同步执行。此方法不可行,不能达到多线程的目的。
接下来我们再次修改代码如下:
static void Main(string[] args)
{
SendEmailMethod(100, 2);//100个操作,2个线程
Console.WriteLine("终于轮到老子了");
Console.ReadLine();
}
1、初始的SendEmailMethod方法
public static void SendEmailMethod(int maxCount, int threadCount)
{
var dataList = new List<EmailSchema>();
for (int i = 0; i < maxCount; i++)
{
dataList.Add(new EmailSchema { Name = "hello" + i.ToString(CultureInfo.InvariantCulture) });
}
//每组数量
int groupCount = dataList.Count / threadCount;
//余数
int leftCount = dataList.Count % threadCount;
//分配给每个线程的邮件
var emailSchemaGroups = new List<List<EmailSchema>>();
for (int i = 0; i < threadCount; i++)
{
emailSchemaGroups.Add(i < leftCount
? dataList.GetRange(i * (groupCount + 1), groupCount + 1)
: dataList.GetRange(leftCount * (groupCount + 1) + (i - leftCount) * groupCount, groupCount));
}
foreach (var emailSchemaGroup in emailSchemaGroups)
{
var thread = new Thread(m =>
{
foreach (var emailData in (List<EmailSchema>)m)
{
Console.WriteLine(emailData.Name);
}
}) { IsBackground = true };
thread.Start(emailSchemaGroup);
//子线程添加Join方法
thread.Join();
}
}
执行结果如下图所示:
主线程的输出“终于轮到老子了”与子线程的输出会随机穿插出现。但是我需要让主线程输出在最后,这如何实现呢?这里考虑到Thread.Join,会阻塞调用线程,直到子线程完成位置。那么在每个子线程都调用Join方法会如何?修改代码如下:
public static void SendEmailMethod(int maxCount, int threadCount)
{
var dataList = new List<EmailSchema>();
for (int i = 0; i < maxCount; i++)
{
dataList.Add(new EmailSchema { Name = "hello" + i.ToString(CultureInfo.InvariantCulture) });
}
//每组数量
int groupCount = dataList.Count / threadCount;
//余数
int leftCount = dataList.Count % threadCount;
//分配给每个线程的邮件
var emailSchemaGroups = new List<List<EmailSchema>>();
for (int i = 0; i < threadCount; i++)
{
emailSchemaGroups.Add(i < leftCount
? dataList.GetRange(i * (groupCount + 1), groupCount + 1)
: dataList.GetRange(leftCount * (groupCount + 1) + (i - leftCount) * groupCount, groupCount));
}
foreach (var emailSchemaGroup in emailSchemaGroups)
{
var thread = new Thread(m =>
{
foreach (var emailData in (List<EmailSchema>)m)
{
Console.WriteLine(emailData.Name);
}
}) { IsBackground = true };
thread.Start(emailSchemaGroup);
//子线程添加Join方法
thread.Join();
}
}
执行结果图:
这里每次执行主线程的输出每次都会在子线程完成之后输出,但是子线程调用Join方法后,不仅仅阻止了主线程的调用,也阻止了其他子线程的调用,故此此处加了Join方法之后是相当于单线程同步执行。此方法不可行,不能达到多线程的目的。
接下来我们再次修改代码如下:
public static void SendEmailMethod(int maxCount, int threadCount)
{
var dataList = new List<EmailSchema>();
for (int i = 0; i < maxCount; i++)
{
dataList.Add(new EmailSchema { Name = "hello" + i.ToString(CultureInfo.InvariantCulture) });
}
//每组数量
int groupCount = dataList.Count / threadCount;
//余数
int leftCount = dataList.Count % threadCount;
//分配给每个线程的邮件
var emailSchemaGroups = new List<List<EmailSchema>>();
for (int i = 0; i < threadCount; i++)
{
emailSchemaGroups.Add(i < leftCount
? dataList.GetRange(i * (groupCount + 1), groupCount + 1)
: dataList.GetRange(leftCount * (groupCount + 1) + (i - leftCount) * groupCount, groupCount));
}
//添加线程集,用于监控线程状态
var threads = new List<Thread>();
foreach (var emailSchemaGroup in emailSchemaGroups)
{
var thread = new Thread(m =>
{
foreach (var emailData in (List<EmailSchema>)m)
{
Console.WriteLine(emailData.Name);
}
}) { IsBackground = true };
thread.Start(emailSchemaGroup);
threads.Add(thread);
}
//创建监控线程
var monitor = new Thread(ThreadMonitor) { IsBackground = true };
monitor.Start(threads);
monitor.Join();//监控线程阻塞主线程
monitor.DisableComObjectEagerCleanup();
}
/// <summary>
/// 线程监控
/// </summary>
public static void ThreadMonitor(object obj)
{
var threads = obj as List<Thread>;
if (threads == null) return;
foreach (var thread in threads)
{
//thread.IsAlive状态等同于thread.ThreadState != ThreadState.Stopped
while(thread.IsAlive) //遍历每个子线程,如果子线程仍然处于活跃状态,则监控线程休眠100毫秒,用于让子线程继续执行。无限循环直到子线程完成
{
Thread.Sleep(100);
}
thread.Abort();
thread.DisableComObjectEagerCleanup();
}
}
执行结果如下图:
我们创建了一个监控线程,用于监控子线程的状态,通过判断子线程的线程状态(IsAlive)判断子线程是否执行完成并杀死线程。监控线程使用Join方法来阻塞主进程,这样就得到想要的结果了。
这里监控线程的作用:
1、阻塞主线程
2、确保子线程操作完成
续:上面的线程资源分配是平均分配的,比如说我有十个需要处理的数据,那么会事先分成2组数据每组5个,分别交给2个线程。这样做可以不用考虑资源会被重复访问,但是会出现一个线程先执行完,另一个还有好几个数据没处理完,线程的利用率不是100%的。现在重新改良方法,如下:
public static void SendEmailMethod(int maxCount, int threadCount)
{
var dataList = new List<EmailSchema>();
for (int i = 0; i < maxCount; i++)
{
dataList.Add(new EmailSchema { Name = "hello" + i.ToString(CultureInfo.InvariantCulture) });
}
Queue<EmailSchema> dataQueue = null;
if (dataList == null || dataList.Count == 0)
{
return;
}
dataQueue = new Queue<EmailSchema>(dataList);
//线程调用
var threads = new List<Thread>();
for (int i = 0; i < threadCount; i++)
{
var thread = new Thread(m =>
{
var newDataQueue = m as Queue<EmailSchema>;
while (newDataQueue != null && newDataQueue.Count > 0)
{
var emailData = newDataQueue.Dequeue();//线程安全的资源,移除并返回栈中第一个对象
SendEmailByData(emailData);
}
}) { IsBackground = true };
thread.Start(dataQueue);
threads.Add(thread);
}
//线程监控
var monitor = new Thread(ThreadMonitor) { IsBackground = true };
monitor.Start(threads);
monitor.Join();
monitor.DisableComObjectEagerCleanup();
}