c# - 在多线程问题中没有得到预期的输出
问题描述
任务描述:
编写一个程序,读取一个正整数值n ( n > 3),然后创建n 个线程(每个线程都有
id
;id
从 1 开始)并一直工作直到它收到一个停止信号。所有n 个线程都在等待信号。每隔一个主线程发送一个随机线程的信号,然后该线程应该打印它id
并返回到等待状态。要求:
所有额外的线程都应该正确完成。在线程函数退出时,应打印有关退出的消息。在线程等待条件变量时,应检查虚假唤醒。只
std::cout
允许文本输出。停止信号是SIGINT (ctrl+c)
。
我为上述问题编写了以下代码,但在输出中,所有线程都没有退出。我无法弄清楚这个问题,因为我是这个话题的新手。任何形式的帮助将不胜感激。
class Program
{
public static void Main()
{
var numberofthreads = Convert.ToInt32(Console.ReadLine());
ProcessingClass myobject = new ProcessingClass();
myobject.createThreads(numberofthreads);
}
}
public class ProcessingClass
{
public Mutex mymutex = new Mutex();
private bool thread_flag = false;
public void createThreads(int numberofthreads)
{
var threads = new List<Thread>(numberofthreads);
for (int i = 0; i < numberofthreads; i++)
{
Thread th = new Thread(() =>
{
threadsworking();
});
th.Name = "Thread" + i;
th.Start(); // <-- .Start() makes the thread start running
threads.Add(th);
}
Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs e) =>
{
var isCtrlC = e.SpecialKey == ConsoleSpecialKey.ControlC;
if (isCtrlC)
{
thread_flag = true;
int num = 1;
foreach (var thread in threads)
{
thread.Join();
Console.WriteLine($"Thread {num} exits");
num++;
}
}
e.Cancel = true;
};
}
public void threadsworking()
{
while (thread_flag == false)
{
mymutex.WaitOne(); // Wait until it is safe to enter.
Console.WriteLine("{0}", Thread.CurrentThread.Name);
Thread.Sleep(1000); // Wait until it is safe to enter.
mymutex.ReleaseMutex(); // Release the Mutex.
}
}
}
解决方案
考虑防止mutex
阻塞线程退出。
当您使用mutex.WaitOne()
它时,它会阻止执行,直到Mutex
它归该线程所有。这对于确保线程对共享资源具有独占控制权非常有帮助。但是,当您想要任意结束这些线程时,例如当您在Console.CancelKeyPress
.
thread.Join()
您可以通过在事件中调用之前和之后记录来查看此效果。
thread_flag = true;
int num = 1;
foreach (var thread in threads)
{
Console.WriteLine($"Joining {thread.Name}");
thread.Join();
Console.WriteLine($"Joined {thread.Name}");
Console.WriteLine($"Thread {num} exits");
num++;
}
当我们执行该日志记录时,它会告诉我们,当您调用Join()
Thread #1 时,您会看到Joining 1
. 然后有一个很长的停顿,其他线程仍在工作,最后所有线程都背靠背加入。
这样做的原因是 - 在Join()
等待线程 1 完成时,线程 1 仍在等待互斥锁。
即使您将thread_flag
标志设置为true
,线程 1 也无法退出,因为它没有获得互斥锁的所有权来执行它的工作并最终退出while()
循环。
我们可以相当简单地解决这个问题。
在等待互斥体时考虑使用超时
当您调用.WaitOne(n)
时,mutex
您可以等待给n
定的毫秒数并放弃对互斥体的所有权。
这将允许对循环进行更频繁的评估while
,随后该threadsworking
方法检查是否应该退出(使用thread_flag
标志)的次数更多。
这是一个简短的示例,如何实现该更改
public void threadsworking()
{
while (thread_flag == false)
{
// wait to enter the mutex, give timeout to prevent blocking
// until mutex opens and use the bool returned to determine
// if we should release the mutex or not
if (mymutex.WaitOne(1))
{
try
{
Console.WriteLine("{0}", Thread.CurrentThread.Name);
Thread.Sleep(1000); // Wait until it is safe to enter.
}
finally
{
// make sure even if we encounter an error the mutex is released
mymutex.ReleaseMutex(); // Release the Mutex.
}
}
// allow other threads to continue their work instead of blocking with a while loop, this is optional depending on the workload
Thread.Yield();
}
}
推荐阅读
- javascript - Web 组件与 JS 中的复合模式有什么关系?
- java - 使代码更具可读性,迭代内部映射
- javascript - 从子组件导航时刷新父组件
- ios - 更改扩展名后base64字符串未打开视频文件
- java - 图库按钮打开相机和图库
- c# - 是否可以在不使用 [JsonIgnore] 的情况下忽略 nswag 中的模型属性?
- sql-server - 组件“Fuzzy Grouping”(40)的版本与该版本的DataFlow不兼容
- python - 使用 Pandas 访问 csv 文件时出现 KeyError
- python - 为 3 通道图像中的每个通道添加相同的值?
- google-app-engine - Google Cloud Datatore Go Client 默认不关闭连接?