一道关于线程的面试题,真的知道的来
一个全局变量tally,两个线程并发执行(代码段都是ThreadProc),问两个线程都结束后,tally取值范围。
int tally = 0;//glable
voidThreadProc()
{
for(inti = 1; i <= 50; i++)
tally += 1;
}
[解决办法]
int g_x = 0;void ThreadFunc1(){ g_x++;}void ThreadFunc2(){ g_x++;}
[解决办法]
我来给个清楚的解释吧 。。。
理论上是这样的:
当线程A与线程B并发执行“tally += 1;”时,这个期间就会发生资源(tally值)的同步问题,具体步骤如下所述:
1. A读取tally值
2. A对tally 进行 +1 操作
3. 线程发生切换
4. B读取tally值
5. B对tally 进行 +1 操作
6. B(往寄存器)写回tally 的新值
7. 线程发生切换
8. A(...)写回tally 的新值
-----------------不知道大家看明白没有,但是以上只是情况之一,还有更混乱的情况大家可以举一反三。
然而实际上的情况是:
目前PC上(包括很多的嵌入式机器)的数学运算(尤其是这样的加法运算)的速率很快的,LZ的题目的正确答案在目前的PC上肯定是100的,因为线程A与线程B均可被完全执行。
后话:
其实关于线程资源的互斥问题,并不可以完全的按照教科书式的进行盲目的lock操作,有很多需要大家仔细琢磨体会的小技术,小细节,这样才能达到安全与效率都完美的境界。
[解决办法]
我的实验:
为了保证问题好复现,循环累加5000次,两个线程的循环将累加操作10000次。
测试一:按照问题测试,最后结果不一定是两个循环的累加和)。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace WindowsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click( object sender,EventArgs e)
{
value = 0;
textBox1.Text = value.ToString();
Thread th1 = new Thread(func);
Thread th2 = new Thread(func);
th1.Start();
th2.Start();
}
//Mutex mvalue = new Mutex();
int value = 0;
void func()
{ int i = 0;
for (i = 0; i < 5000; i++)
{
//mvalue.WaitOne();
value++;
//mvalue.ReleaseMutex();
// 显示刷新
setText();
// 给刷新留出时间
Thread .Sleep(1);
}
}
delegate void setTextCallBack();
void setText()
{
if (textBox1.InvokeRequired)
{
setTextCallBack d = newsetTextCallBack(setText);
this .Invoke(d);
}
else
{
textBox1.Text = value.ToString();
}
}
private void button2_Click( object sender,EventArgs e)
{
textBox1.Text = value.ToString();
}
}
}
测试二:加上互斥信号对运算操作进行保护结果就是两个循环的累加和
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace WindowsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click( object sender,EventArgs e)
{
value = 0;
textBox1.Text = value.ToString();
Thread th1 = new Thread(func);
Thread th2 = new Thread(func);
th1.Start();
th2.Start();
}
Mutex mvalue = new Mutex();
int value = 0;
void func()
{ int i = 0;
for (i = 0; i < 5000; i++)
{
mvalue.WaitOne();
value++;
mvalue.ReleaseMutex();
// 显示刷新
setText();
// 给刷新留出时间
Thread .Sleep(1);
}
}
delegate void setTextCallBack();
void setText()
{
if (textBox1.InvokeRequired)
{
setTextCallBack d = newsetTextCallBack(setText);
this .Invoke(d);
}
else
{
textBox1.Text = value.ToString();
}
}
private void button2_Click( object sender,EventArgs e)
{
textBox1.Text = value.ToString();
}
}
}