Skip to content

Conversation

guix-thu
Copy link

@guix-thu guix-thu commented Jul 27, 2024

第一题和第二题

*/
SetParent(A, this);
SetParent(B, this);
val = ExprFunc(ExprA.Val, ExprB.Val);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

显然,由于被包含在构造函数中,这一段计算将会同步化,这并不符合题意。
注意,这一题最重要的要求就是异步计算,计算结果究竟是否正确其实还在其次。


public void WaitForAvailable()
{
while (!Available) ;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个忙等待虽然可行,但是大量使用可能会使你的电脑发烫(
你可以尝试使用 Monitor.WaitMonitor.Pulse 来避免忙等待。

lock (lockVal)
{
Unavilablize();
val = ExprFunc(ExprA.Val, ExprB.Val);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个位置,其本身是没有问题的;但是结合锁的使用来看,问题很大。

我们可以认为,计算所需要的时间是很久的;甚至于从开始更新到即将读取之时,程序都无法完成计算任务。对于这种情况,我们应当认为我们确实是在数据尚未更新时就想要读取,那么就有了 WaitForAvailable;如果用户不使用 WaitForAvailable,那么就不保证值的正确性。

但是,你现在将整个计算过程置于锁 lockVal 之中,由于开始更新在前,在一定的可能下 (如主线程中间也进行了一些微小的处理),使得将要读取时锁被计算任务控制,那么将会一直阻塞直至计算任务的完成 (当然,这个值也不一定就是正确的)。我们只希望锁是用来保护读取行为的;换言之,在这一场景中,锁只应当用于资源互斥而非条件同步

一个示例,随机地输出 0 00 2121 49

ValueExpr a = new(1);
ValueExpr b = new(2);
ValueExpr c = new(3);
ValueExpr d = new(4);
AddExpr add1 = new(a, b);
AddExpr add2 = new(c, d);
MulExpr mul = new(add1, add2);
//mul.WaitForAvailable();
Console.WriteLine(mul.Val);
a.NewVal = 5;
//mul.WaitForAvailable();
Console.WriteLine(mul.Val);

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

还有一个问题,是关于用时的。

如果使用原先的阻塞式构造函数,整个构造过程耗费3T (T为单次计算用时),而后面第二次等待耗费2T,这很符合预期。

但是如果采用非阻塞式构造函数,构造过程几乎不耗费用时;第一次等待耗费2T或3T,随后第一次读取时等待锁2T或1T,也就是两段总共4T;第二次等待耗费2T,然后读取不需要等待锁。

由于这里必须采用非阻塞的构造函数,因此上述的奇怪现象引出一个问题——WaitForAvailable 或者锁 lockVal 有着潜在的问题,需要用户等待两次才能读取到值。我没看出来问题在哪,但显然这个用时是无论如何不正确的。

@asdawej asdawej added the revision needed This PR need further revision label Aug 3, 2024
@asdawej
Copy link
Owner

asdawej commented Aug 3, 2024

/* 
* 直接对parent进行修改,而不是调用Register方法
* Register操作不可能对子树造成影响,只会对父节点及以上的节点值造成影响
* 而此处构造函数中已经进行了计算,使用Register造成了重复计算,白白浪费两倍时间 
*/

这段思考很好。


/// <summary>
/// 结点值是否可用,用于判断结点值是否已经更新完毕
/// </summary>
/// <value></value>
public bool Available { get; protected set; } = true;

增加 Available 属性,非常的富有勇气,因为在以高灵敏度著称的异步计算模型中,Available 属性真正有效的可能性非常低,毕竟 Available 属性的变化速度不一定比得上数据的变化速度……


AddExpr 进一步变成 BinaryExpr,虽然很容易想到,但毕竟是要动手改的,所以还是值得表扬。


第二题基本满足要求。

@asdawej asdawej added the checked This PR is checked as pass label Aug 3, 2024
@guix-thu
Copy link
Author

修改了第一题,但是有千分之几的概率锁在Wait处,实在调不动了:(

@asdawej asdawej removed the revision needed This PR need further revision label Sep 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
checked This PR is checked as pass
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants