出大事儿了,今天出现一个非常诡异的情况。明明表达式的值是false。但是执行结果却如下图这样。我的世界观瞬间就倒塌了。
这是什么情况。
同样的代码在同事的机器上运行却没有问题,到现在为止还没有找到原因。
我的环境是win7x64同事的是win7x32
今天早上装了13个补丁,不知道是不是和这有关。
有遇到同样问题的同学望告知。
又测试了一下,上面两个If能正常执行。到第三个又出问题了。
彻底无语了。这是为什么
有可能是源代码与调试进程不匹配,将所有代码都重新编译一次。
试过了以下办法:
1、清理解决方案
2、重开解决方案
3、重启计算机
还是不能解决问题。
@Crisqiu: 你这样写 if((int)task.TaskType != (int)TaskType.Message)
你是调试的 Debug,还是 Realease?
@程序猿.码农: task.TaskType本身就是int32类型的。按照你这个写法可以解决。但是实在是超出了我的认知范围。我只能说这是一个BUG
@程序猿.码农: 调试的是debug
现在又有这样的情况
这是为什么啊为什么
@Crisqiu: 这不是BUG,如果是BUG,那么所有的关于使用枚举的代码都会出错,但事实上不是这样,只有你写的出了错,而且很可能仅仅是你的这段代码。
@程序猿.码农: TaskType 是你自己定义的枚举吗?
@程序猿.码农: 是的
@Crisqiu: 你把枚举名称由 TaskType 重命名为 TaskTypeEnum
@程序猿.码农: 这个改动太大了,影响到项目中很多的代码,暂时不能改。能说说您这样考虑的原因吗?
@Crisqiu: 你建一个 Console 的项目,运行下面的代码:
public enum TaskType
{
None = 0,
Error = 1,
Message = 2,
}
public class Task
{
public Int32 TaskType { get; set; }
}
class Program
{
static void Main(string[] args)
{
Task task = new Task() { TaskType =2};
if (task.TaskType != (int)TaskType.Message)
throw new NotSupportedException();
}
}
@程序猿.码农: 你只贴了部分代码,我怀疑你是用了多线程,而没有对task变量的TaskType属性做保护,导致task.TaskType的值在比较时已经被更改。
@程序猿.码农: 新建的项目中没有出现这个问题。已经超出了我的认知范围了。看来不是编译器出的问题。
@程序猿.码农: 我确实是用到了多线程,但是如何解释前面那几种可以正常执行的情况呢。请指教。而且如何解释在win7 x86下面能够正常执行的情况呢。
@Crisqiu: 你现在的问题不是让我如何解释,如果最终原因是因为不安全的多线程代码所致,那么我可以给你解释,而且还可以给你举例一些代码,在x86,x64,单核,双核,甚至是相同环境下会出现的“随机”行为。因此,我们要先找到问题,我建议你这样写:int nType = task.TaksType;if(nType != (int)TaskType.Message) throw new NotSupportedExcetion(string.Format("不支持的任务类型:old value :{0},new value {1}",nType,task.TaskType));
@程序猿.码农:
这段代码是通过一个Timer每30秒执行一次 private void BackgroundWork(object state) { foreach (TimerService service in this.Services) { if (service.CanDoExecute(DateTime.Now)) { ThreadPool.QueueUserWorkItem(service.Execute); } } }
namespace ChmsBgServer.TimerServices { using System; using System.Collections.Generic; using System.Linq; using System.Text; using Teda.Chms.Infrastructure.Data; using Teda.Chms.Domain.Services; using Teda.Chms.Application.ServiceManagement; using Teda.Chms.Infrastructure.IoC; /// <summary> /// 定时服务基类 /// </summary> public abstract class TimerService { /// <summary> /// 执行该服务的时间片 /// </summary> protected IList<TimeSpan> ExecuteTimes { get; set; } /// <summary> /// 最后一次执行时间 /// </summary> public DateTime? LastExecuteTime { get; protected set; } /// <summary> /// 服务名称 /// </summary> public abstract string ServiceName { get; } /// <summary> /// 执行间隔 /// </summary> public int Interval { get; private set; } /// <summary> /// 同步对象 /// </summary> public abstract object SyncObject { get; } /// <summary> /// 构造函数 /// </summary> protected TimerService() { ITimerServiceExecuteMgtService service = IoCFactory.Instance.CurrentContainer.Resolve<ITimerServiceExecuteMgtService>(); TimerServiceExecuteInfo info = service.GetServiceExecuteInfo(this.ServiceName); if (info.LastExecuteDate.HasValue) this.LastExecuteTime = info.LastExecuteDate; } /// <summary> /// 构造函数 /// </summary> /// <param name="interval"></param> protected TimerService(int interval) : this() { this.Interval = interval; } /// <summary> /// 构造函数 /// </summary> /// <param name="exeTimes">执行服务的时间片</param> public TimerService(IList<TimeSpan> exeTimes) : this() { if (exeTimes == null) throw new ArgumentNullException(); if (exeTimes.Count == 0) throw new ArgumentException(); ExecuteTimes = exeTimes.OrderBy(o => o.Ticks).ToList(); } /// <summary> /// 执行定时方法 /// </summary> /// <param name="state"></param> public void Execute(object state) { lock (SyncObject) { DateTime now = DateTime.Now; if (!CanDoExecute(now)) return; //执行子类定时执行方法 DoWork(); this.LastExecuteTime = now; ITimerServiceExecuteMgtService service = IoCFactory.Instance.CurrentContainer.Resolve<ITimerServiceExecuteMgtService>(); service.UpdateLastExecuteDate(this.ServiceName, this.LastExecuteTime.Value); } } /// <summary> /// 是否可以执行 /// </summary> /// <returns></returns> public virtual bool CanDoExecute(DateTime now) { bool isFound = false; int foundIndex = 0; if (!LastExecuteTime.HasValue) isFound = true; else { for (int i = 0; i < this.ExecuteTimes.Count; i++) { if (LastExecuteTime.Value.Date < now.Date || LastExecuteTime.Value.TimeOfDay < this.ExecuteTimes[i]) { isFound = true; foundIndex = i; break; } } } if (isFound) { return (now.TimeOfDay >= this.ExecuteTimes[foundIndex]); } else return false; } /// <summary> /// 具体服务所执行的工作 /// </summary> /// <param name="context"></param> public abstract void DoWork(); } }
每个定时服务都是实现这个基类中的DoWork方法。
@Crisqiu: 你给出的代码没有什么有用的线索,你应该找到给task.TaskType 赋值的所有语句,观察它们会在怎样的并发条件下执行。如果排查起来很困难,我建议把 TaskType 设置为只读属性,并且只能通过Task的构造函数赋值,并且Task不支持克隆。这样,我们就能很自信的说,task.TaskType只被赋值过一次,如果它不等于 2,那它就真的不等于 2,而不是被谁偷偷的修改过。
@程序猿.码农: 综合你对下面网友的回复,我猜想你的task是这么得来的:
Task task = dbContext.Tasks.Where(o=>o.Id =2);
那么正确传递参数方式是:
Task runTask = task.Clone();
Execute(runTask);
这样,我就能保证你不会再怀疑执行结果了。
@程序猿.码农: 从AnyCPU改成x86后程序能正常执行了。但是还是没有找到原因。
Task和TaskType的代码发来看看。
Task就是一个实体,属性TaskType只有getter和setter
类型TaskType是一个枚举Message是2
@Crisqiu: 你的getter是不是有副作用?
@水牛刀刀:
/// <summary> /// 任务类型 /// </summary> public int TaskType { get; set; }
应该没有什么副作用吧
@Crisqiu: 看看你项目的platform是不是Any CPU
@水牛刀刀: 是得
@Crisqiu: 你其他线程是不是可能会修改这个属性的值?
@水牛刀刀: 不会的,这个数据是从数据库中加载的,包括数据加载到处理完成后再保存到数据库中都是在一个线程中操作的。其他线程不会直接操作到这个线程中的数据。
@Crisqiu: 你qq告诉我
namespace ChmsBgServer.TimerServices { using System; using System.Collections.Generic; using System.Linq; using System.Text; using Teda.Chms.Infrastructure.Data; using Teda.Chms.Domain.Services; using Teda.Chms.Application.ServiceManagement; using Teda.Chms.Infrastructure.IoC; /// <summary> /// 定时服务基类 /// </summary> public abstract class TimerService { /// <summary> /// 执行该服务的时间片 /// </summary> protected IList<TimeSpan> ExecuteTimes { get; set; } /// <summary> /// 最后一次执行时间 /// </summary> public DateTime? LastExecuteTime { get; protected set; } /// <summary> /// 服务名称 /// </summary> public abstract string ServiceName { get; } /// <summary> /// 执行间隔 /// </summary> public int Interval { get; private set; } /// <summary> /// 同步对象 /// </summary> public abstract object SyncObject { get; } /// <summary> /// 构造函数 /// </summary> protected TimerService() { ITimerServiceExecuteMgtService service = IoCFactory.Instance.CurrentContainer.Resolve<ITimerServiceExecuteMgtService>(); TimerServiceExecuteInfo info = service.GetServiceExecuteInfo(this.ServiceName); if (info.LastExecuteDate.HasValue) this.LastExecuteTime = info.LastExecuteDate; } /// <summary> /// 构造函数 /// </summary> /// <param name="interval"></param> protected TimerService(int interval) : this() { this.Interval = interval; } /// <summary> /// 构造函数 /// </summary> /// <param name="exeTimes">执行服务的时间片</param> public TimerService(IList<TimeSpan> exeTimes) : this() { if (exeTimes == null) throw new ArgumentNullException(); if (exeTimes.Count == 0) throw new ArgumentException(); ExecuteTimes = exeTimes.OrderBy(o => o.Ticks).ToList(); } /// <summary> /// 执行定时方法 /// </summary> /// <param name="state"></param> public void Execute(object state) { lock (SyncObject) { DateTime now = DateTime.Now; if (!CanDoExecute(now)) return; //执行子类定时执行方法 DoWork(); this.LastExecuteTime = now; ITimerServiceExecuteMgtService service = IoCFactory.Instance.CurrentContainer.Resolve<ITimerServiceExecuteMgtService>(); service.UpdateLastExecuteDate(this.ServiceName, this.LastExecuteTime.Value); } } /// <summary> /// 是否可以执行 /// </summary> /// <returns></returns> public virtual bool CanDoExecute(DateTime now) { bool isFound = false; int foundIndex = 0; if (!LastExecuteTime.HasValue) isFound = true; else { for (int i = 0; i < this.ExecuteTimes.Count; i++) { if (LastExecuteTime.Value.Date < now.Date || LastExecuteTime.Value.TimeOfDay < this.ExecuteTimes[i]) { isFound = true; foundIndex = i; break; } } } if (isFound) { return (now.TimeOfDay >= this.ExecuteTimes[foundIndex]); } else return false; } /// <summary> /// 具体服务所执行的工作 /// </summary> /// <param name="context"></param> public abstract void DoWork(); } }
@水牛刀刀: 这个是基类,所有子类的任务都是实现DoWork方法
我遇到过一次类似的诡异情况是,task.TaskType或TaskType.Message是个属性(函数),每次Evaluation的结果会变化。
加上大括号试试
加上大括号也不行。这个问题在x86环境的电脑上不存在,以前也没有发现这个问题,以前代码也都是测试过的。今天执行的时候就出现问题了。
@Crisqiu: 为什么不直接用 task.TaskType != TaskType.Message ?
@dudu: 因为用到了Entityframework4.1 还不支持enum类型
@Crisqiu: 我们也用了Entity Framework,也不支持enum,我们是这样处理的:
1. 实体类定义
public class BlogPost { public PostType PostType { get { return (PostType)PostTypeEf; } set { PostTypeEf = (int)value; } } public int PostTypeEf { get; set; } }
2. EF映射
modelBuilder.Entity<BlogPost>().Ignore(x => x.PostType); modelBuilder.Entity<BlogPost>().Property(x => x.PostTypeEf).HasColumnName("PostType");
@dudu: 这个办法挺不错的,谢谢分享