为什么从另一个线程更新控件的某些属性是安全的?

jon*_*ana 2 .net c# vb.net

System.Windows.Forms.Control与其他不安全的属性相比,为什么从另一个线程更新 的部分属性是安全的,并且程序员必须使用Delegate?

例如,ForeColor比较Text.

有人可以从设计的角度解释这一点吗?

TnT*_*nMn 5

要回答你的问题,为什么一些属性访问引发非法跨线程错误和其他不这样做,你可以参考源代码System.Windows.Forms.Control。它需要进行一些挖掘,但它确实看起来好像建议某些访问是线程安全的,例如获取Text属性但设置它不是。

实际上,将所有控制访问视为非线程安全的传统智慧是最佳实践。

public virtual string Text {
    get {
        if (CacheTextInternal) {
            return(text == null) ? "" : text;
        }
        else {
            return WindowText;
        }
    }

    set {
        if (value == null) {
            value = "";
        }

        if (value == Text) {
            return;
        }

        if (CacheTextInternal) {
            text = value;
        }
        WindowText = value;
        OnTextChanged(EventArgs.Empty);

        if( this.IsMnemonicsListenerAxSourced ){
            for( Control ctl = this; ctl != null; ctl = ctl.ParentInternal ) {
                ActiveXImpl activeXImpl = (ActiveXImpl)ctl.Properties.GetObject(PropActiveXImpl);
                if( activeXImpl != null ) {
                    activeXImpl.UpdateAccelTable();
                    break;
                }
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

请注意上面代码中内部 WindowText 属性的使用。

    /// <devdoc>
    ///     The current text of the Window; if the window has not yet been created, stores it in the control.
    ///     If the window has been created, stores the text in the underlying win32 control.
    ///     This property should be used whenever you want to get at the win32 control's text. For all other cases,
    ///     use the Text property - but note that this is overridable, and any of your code that uses it will use
    ///     the overridden version in controls that subclass your own.
    /// </devdoc>
    internal virtual string WindowText {
        get {

            if (!IsHandleCreated) {
                if (text == null) {
                    return "";
                }
                else {
                    return text;
                }
            }

            using (new MultithreadSafeCallScope()) {

                // it's okay to call GetWindowText cross-thread.
                //

                int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(window, Handle));

                // Check to see if the system supports DBCS character
                // if so, double the length of the buffer.
                if (SystemInformation.DbcsEnabled) {
                    textLen = (textLen * 2) + 1;
                }
                StringBuilder sb = new StringBuilder(textLen + 1);
                UnsafeNativeMethods.GetWindowText(new HandleRef(window, Handle), sb, sb.Capacity);
                return sb.ToString();
            }
        }
        set {
            if (value == null) value = "";
            if (!WindowText.Equals(value)) {
                if (IsHandleCreated) {
                    UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value);
                }
                else {
                    if (value.Length == 0) {
                        text = null;
                    }
                    else {
                        text = value;
                    }
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

注意使用MultithreadSafeCallScopeget代码。还要注意Handle会抛出跨线程错误的属性的使用;我相信Handle财产充当守门员支票跨线程访问。

public IntPtr Handle {
    get {
        if (checkForIllegalCrossThreadCalls &&
            !inCrossThreadSafeCall &&
            InvokeRequired) {
            throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall,
                                                             Name));
        }

        if (!IsHandleCreated)
        {
            CreateHandle();
        }

        return HandleInternal;
    }
}
Run Code Online (Sandbox Code Playgroud)