Multithreading 什么是多线程操作和不操作?
我正在将我新发现的线程知识应用到任何地方,并获得了许多惊喜 示例:Multithreading 什么是多线程操作和不操作?,multithreading,language-agnostic,Multithreading,Language Agnostic,我正在将我新发现的线程知识应用到任何地方,并获得了许多惊喜 示例: 我使用线程在 数组。结果每一天都不一样 时间问题是我所有的 线程正在更新相同的 变量和未同步 哪些是已知的线程问题 使用时应注意什么 线程 什么是好的多线程资源 请举例说明 旁注:(我将我的程序thread\u add.java重命名为thread\u random\u number\u generator.java:-)在多线程环境中,您必须注意同步,这样两个线程不会通过同时执行修改来破坏状态。否则,您可以在代码中设置竞态
我使用线程在 数组。结果每一天都不一样 时间问题是我所有的 线程正在更新相同的 变量和未同步
- 哪些是已知的线程问题
- 使用时应注意什么 线程
- 什么是好的多线程资源
- 请举例说明
旁注:
(我将我的程序
thread\u add.java
重命名为thread\u random\u number\u generator.java
:-)在多线程环境中,您必须注意同步,这样两个线程不会通过同时执行修改来破坏状态。否则,您可以在代码中设置竞态条件(例如,请参见)。您还必须调度线程以执行各种任务。然后,您必须确保同步和调度不会导致死锁,多个线程将无限期地相互等待
同步
像增加计数器这样简单的操作需要同步:
counter += 1;
假设以下事件序列:
初始化为0计数器
- 线程A将
从内存检索到cpu(0)计数器
- 上下文切换
- 线程B将
从内存检索到cpu(0)计数器
- 线程B增加cpu上的计数器
- 线程B将
计数器
从cpu写回内存(1)
- 上下文切换
- 线程A增加cpu上的计数器
- 线程A将
计数器
从cpu写回内存(1)
计数器
为1,但两个线程都试图增加它。对计数器的访问必须通过某种锁定机制进行同步:
lock (myLock) {
counter += 1;
}
只允许一个线程在锁定块内执行代码。执行此代码的两个线程可能会导致以下事件序列:
- 计数器已初始化为0
- 线程A获取
myLock
- 上下文切换
- 线程B试图获取
,但必须等待myLock
- 上下文切换
- 线程A将
从内存检索到cpu(0)计数器
- 线程A增加cpu上的计数器
- 线程A将
计数器
从cpu写回内存(1)
- 线程A释放myLock
- 上下文切换
- 线程B获取
myLock
- 线程B将
从内存检索到cpu(1)计数器
- 线程B增加cpu上的计数器
- 线程B将
计数器
从cpu写回内存(2)
- 线程B释放myLock
计数器
为2
日程安排
调度是另一种同步形式,您必须使用事件、信号量、消息传递等线程同步机制来启动和停止线程。下面是C#中的一个简化示例:
您会注意到,对this.task
的访问可能没有正确同步,工作线程无法将结果返回主线程,并且无法向工作线程发出终止的信号。所有这些都可以在一个更详细的例子中得到纠正
死锁
死锁的一个常见示例是,当您有两个锁,并且不小心获取它们时。在某一点上,您在lock2
之前获得lock1
:
public void f() {
lock (lock1) {
lock (lock2) {
// Do something
}
}
}
在另一点上,您在lock1
之前获取lock2
:
public void g() {
lock (lock2) {
lock (lock1) {
// Do something else
}
}
}
让我们看看这会如何死锁:
- 线程A调用
f
- 线程A获取
lock1
- 上下文切换
- 线程B调用
g
- 线程B获取
lock2
- 线程B试图获取
,但必须等待lock1
- 上下文切换
- 线程A试图获取锁2,但必须等待
- 上下文切换
此时,线程A和线程B正在互相等待并处于死锁状态。在.Net中,当我开始尝试进入多线程时,有一件事让我感到惊讶,那就是除了创建UI控件的线程之外,您无法直接从任何线程更新UI控件 有一种方法可以解决这个问题,那就是使用Control.Invoke方法来更新另一个线程上的控件,但是第一次不是100%明显
要记住的最重要的事情是:你真的需要多线程吗?需要注意的一件重要事情(使用多核和CPU)是。除了向你指出谷歌,我不能给你举其他例子。搜索线程基础,线程同步,你会得到比你知道更多的点击率 线程的基本问题是线程彼此不了解-因此它们会很高兴地踩在彼此的脚趾上,就像两个人试图通过一扇门一样,有时它们会一个接一个地通过,但有时它们都试图同时通过,并且会被卡住。这很难重现,很难调试,有时还会引起问题。如果您有线程并看到“随机”故障,这可能就是问题所在 因此,需要注意共享资源。如果你和你的朋友想要一杯咖啡,但是只有一个勺子,你不能同时使用它,你们中的一个将不得不等待另一个。用于“同步”访问共享勺子的技术是锁定。你
public void g() {
lock (lock2) {
lock (lock1) {
// Do something else
}
}
}