日期:2014-05-18  浏览次数:20759 次

一道关于线程的面试题,真的知道的来

一个全局变量tally,两个线程并发执行(代码段都是ThreadProc),问两个线程都结束后,tally取值范围。

int tally = 0;//glable

voidThreadProc()

{

  for(inti = 1; i <= 50; i++)

  tally += 1;

}

------解决方案--------------------
探讨
引用:

引用:
个人认为就是100

那说明两点,
1,你对共享资源的并发访问可能产生的问题一点概念都没有
2,你缺乏动手能力,如果你愿意自己写代码测试一下,你就会发现自己错了。


王道应该在对+=的处理吧!
如果编译器的是把tally拷出来放到另外一个寄存器后+1,然后再赋值回去,那么肯定有线程的问题;
倘若只是在原来保存的位置直接+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)