2012-03-12 44 views
12

Trong dự án hiện tại của tôi có một lớp Mẫu mà trông như thế này:Giải quyết sự cố "Không thể truy cập đối tượng được xử lý". ngoại lệ

public partial class FormMain : Form 
{ 

    System.Timers.Timer timer; 
    Point previousLocation; 
    double distance; 

    public FormMain() 
    { 
     InitializeComponent(); 

     distance = 0; 
     timer = new System.Timers.Timer(50); 
     timer.AutoReset = true; 
     timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed); 
     timer.Start(); 
    } 

    private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
    { 
     if (previousLocation != null) 
     { 
      // some code 

      UpdateDistanceLabel(distance); 
      UpdateSpeedLabel(v); 
     } 

     previousLocation = Cursor.Position; 
    } 

    private void UpdateDistanceLabel(double newDistance) 
    { 
     if (!lblDistance.IsDisposed && !IsDisposed) 
     { 
      Invoke(new Action(() => lblDistance.Text = String.Format("Distance: {0} pixels", newDistance))); 
     } 
    } 

    private void UpdateSpeedLabel(double newSpeed) 
    { 
     if (!lblSpeed.IsDisposed && !IsDisposed) 
     { 
      Invoke(new Action(() => lblSpeed.Text = String.Format("Speed: {0} pixels per second", newSpeed))); 
     } 
    } 

} 

Như bạn thấy, tôi đang sử dụng một đối tượng System.Timers.Timer. Tôi biết tôi có thể sử dụng System.Windows.Forms.Timer, nhưng tôi khá quan tâm đến lý do tại sao tôi vẫn nhận được ngoại lệ được hiển thị trong tiêu đề. Nó được ném vào cuộc gọi Invoke trong phương thức UpdateDistanceLabel. Điều gì làm tôi bối rối là nó nói "Không thể truy cập đối tượng được xử lý: FormMain" mặc dù tôi đang kiểm tra xem nó có được xử lý hay không. Vì vậy, điều đó không nên xảy ra. Tôi cũng đã cố gắng xử lý các đối tượng bộ đếm thời gian trong sự kiện FormClosing cũng như ghi đè Vứt bỏ (bool) và xử lý nó ở đó, cả hai đều tiếc là không giúp gì cả. Ngoài ra, ngoại lệ không phải lúc nào cũng bị ném, được cho là chỉ khi bộ hẹn giờ xảy ra cháy trong khi chương trình đang thoát. Nó vẫn xảy ra rất nhiều.

Tôi đã thấy rằng có rất nhiều chủ đề về điều này, nhưng tôi đã thử các giải pháp được đăng ở đó, hầu hết trong số họ liên quan đến việc kiểm tra thuộc tính IsDisposed - không hoạt động đối với tôi. Vì vậy, tôi đoán tôi đang làm điều gì đó sai trái.

Vì vậy, câu hỏi của tôi: Tại sao mã được đăng trên lửa là ngoại lệ mặc dù tôi đang kiểm tra xem các đối tượng tôi đang truy cập có được xử lý hay không?

Trả lời

9

Có hai cách giải quyết: hoặc nuốt ngoại lệ và nguyền rủa Microsoft vì không bao gồm phương thức TryInvokeTryBeginInvoke hoặc sử dụng khóa để đảm bảo rằng không được thực hiện đối tượng khi đang sử dụng và không cố gắng được thực hiện để sử dụng đối tượng trong khi đang thực hiện Dispose. Tôi nghĩ rằng nuốt ngoại lệ có lẽ tốt hơn, nhưng một số người có phản ứng nội tạng chống lại những thứ như vậy, và việc sử dụng khóa có thể tránh được việc có ngoại lệ xảy ra ngay từ đầu.

9

Một vấn đề là bạn đang thực hiện kiểm tra trên bộ hẹn giờ, trước khi gọi Invoke. Có một điều kiện chủng tộc có thể xảy ra, trong đó Biểu mẫu có thể được xử lý sau khi kiểm tra của bạn và trước khi hành động được gọi được thực thi.

Bạn nên thực hiện kiểm tra bên trong phương thức (biểu thức lambda trong trường hợp của bạn) được gọi là Invoke.

Một vấn đề khác có thể xảy ra là bạn đang truy cập Cursor.Position trên chuỗi hẹn giờ. Tôi không chắc chắn nếu điều này là hợp lệ - Tôi sẽ làm điều này trên các chủ đề chính. Mã của bạn cũng bao gồm chú thích //some code - vì vậy bạn có lẽ đã bỏ qua một số mã mà bạn cũng cần phải kiểm tra.

Nhìn chung, bạn có thể nên sử dụng số System.Windows.Forms.Timer tốt hơn.

+0

Cảm ơn. Đó là những gì tôi đã làm bây giờ, tuy nhiên, nó không ảnh hưởng đến hành vi rất nhiều. – haiyyu

6

Đây là giải pháp của tôi để ngoại trừ của bạn nếu bạn quan tâm:

private void FormMain_FormClosing(object sender, FormClosingEventArgs e) 
     { 
      timer.Stop(); 
      Application.DoEvents();  
     } 

.Stop() mà không .DoEvents() là không đủ, vì nó sẽ định đoạt đối tượng mà không cần chờ cho thread của bạn để kết thúc của nó công việc.

+0

Có, nhanh chóng và bẩn nhưng nó sẽ làm các trick. Và nó cho thấy nguyên nhân gốc rễ của anh ta. –

+0

Gọi 'Application.DoEvents' sẽ không đảm bảo rằng bạn tránh được tình trạng cuộc đua tiềm năng, mặc dù điều này có thể sẽ khiến nó ít có khả năng xảy ra hơn. – Joe

+0

Ông cũng có thể thêm Thread.Sleep (100); sau .DoEvents(), tuy nhiên, nó thậm chí còn bẩn hơn:/ –

0

Tạo hai boolean gọi là 'StopTimer' và 'TimerStopped' với trạng thái ban đầu được đặt thành false. Đặt thuộc tính AutoReset của bộ hẹn giờ thành false. Sau đó, định dạng phương thức Đã trôi qua thành các mục sau:

Invoke((MethodInvoker)delegate { 
    // Work to do here. 
}); 
if (!StopTimer) 
    timer.Start(); 
else 
    TimerStopped = true; 

Bằng cách này bạn đang tránh tình trạng cuộc đua, kiểm tra xem bộ hẹn giờ có tiếp tục và báo cáo khi phương pháp đã kết thúc hay không.

Bây giờ thiết lập phương pháp FormClosing của bạn như thế này:

if (!TimerStopped) 
{ 
    StopTimer = true; 
    Thread waiter = new Thread(new ThreadStart(delegate { 
     while (!TimerStopped) { } 
     Invoke((MethodInvoker)delegate { Close(); }); 
    })); 
    waiter.Start(); 
    e.Cancel = true; 
} 
else 
    timer.Dispose(); 

Nếu bộ đếm thời gian đã không ngừng được nêu ra, một sợi được đưa ra để chờ đợi cho đến khi nó đã làm như vậy và sau đó cố gắng để đóng mẫu một lần nữa.

Các vấn đề liên quan