一道关于线程的面试题,真的知道的来
一个全局变量tally,两个线程并发执行(代码段都是ThreadProc),问两个线程都结束后,tally取值范围。
int tally = 0;//glable
voidThreadProc()
{
for(inti = 1; i <= 50; i++)
tally += 1;
}
[解决办法]
[解决办法]
其实CPU的运算器就是最基础的逻辑处理,加减乘除与非或,等操作,从寄存器里把数读进来,然后累加,再送回寄存器。
所以,数据想做运算,就必须送到CPU(运算器)里去做。
[解决办法]
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.Start();
t2.Start();
private int id = 0;
private void test()
{
for (int i = 0; i < 50; i++)
{
id +=1;
//Thread.Sleep(1);
}
Console.WriteLine("累加50次后的结果{0}", id);
}
如果楼上做的是这种测试,那么请注意一件事情。如果你的输出是
累加50次后的结果50
累加50次后的结果100
那么你做的测试根本没有意义,因为这两个线程根本没有发生切换,也就没有发生id这个资源并发访问的情况。跟单线程做是没有区别的。
想模拟多线程,就把注释去掉,模拟一下线程切换。
[解决办法]
还是分析底层最管用,汇编
例如:
- C# code
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();
}
}
}