none
我想实现silverlight2中的webservice同步调用,但是下面的程序打死都过不去,高手能帮忙看一下吗? RRS feed

  • 问题

  • 因为业务逻辑必须要求同步调用服务,成功之后才能继续,因此我试着用等待信号量的方式实现同步ws的调用。
    但是,不知道为什么,只要我开始等待信号量,WS的调用就永远不会结束,complate事件就永远不会发生。

    我试着把调用WS的代码放在另一个线程中执行,也是同样的问题。

    我调试中发现,新线程正常启动了,对WS的调用也是启动了的。但是complate事件总是不会被调用。
    一但我去掉了对信号量的等待,就正常了(当然这样就无法实现同步调用的效果了)。

    有没有高手可以找到原因?sl里的线程结构到底是怎么回事?

    下午要开会,没办法继续做实验了。

    下面是代码:

    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Threading;
    using SilverlightApplication2.ServiceReference1;
    
    namespace SilverlightApplication2
    {
        /// 
        /// 这是助手类,用于方便地扩展原来的webservice代理类。
        /// 
        public static class ServicesHelper
        {
            /// 
            /// 内部类,用于保存一次调用过程中的各种参数上下文。
            /// 
            class CallState
            {
                public AutoResetEvent _AutoResetEvent = null; // 信号事件
                public object _Result = null;                 // 用于回传结果.
                public Exception _Error = null;               // 用于回传异常.
                public bool _Cancelled = false;               // 用于回传是否取消调用标志
                public object _oParam;                        // 用于传入参数.
                public EmployeeServiceSoapClient _proxy;      // 用于记录当前调用时用的代理对象.
    
    
                public CallState(bool bInitStat, object oParam, EmployeeServiceSoapClient proxy)
                {
                    this._AutoResetEvent = new AutoResetEvent(bInitStat);
                    this._oParam = oParam;
    
                    this._proxy = proxy;
                }
            }
    
            public static ServiceReference1.Empno GetEmpInfo(
                this ServiceReference1.EmployeeServiceSoapClient proxy, 
                string sEmpno)
            {
                // 先准备各种参数.
                CallState cs = new CallState(false, (object)sEmpno, proxy);
    
                // 用第二个线程执行调用. 一开始我是直接调用的, 不成功, 又试着用这种启动第二个线程的方法. 
                Thread th = new Thread(new ParameterizedThreadStart(ThreadGetEmpInfo));
    
                // 启动线程. 传入参数.
                th.Start(cs);
                
                // 等着参数中的信号被设置。 这里会阻塞本线程的执行。在执行完成webservice的调用后,这个信号会被设置。
                // 但是,实际上这个等等一但开始, 完成调用的proxy_GetEmployeeInfoCompleted就永远不会进入了.不知道为什么.
                cs._AutoResetEvent.WaitOne();
    
                // 到这里时, 就是已经等到事件量被设置了,从参数里取出有关的结果,向调用者返回。
                if (cs._Error != null)
                {
                    throw cs._Error;
                }
    
                if (cs._Cancelled)
                {
                    throw new Exception("操作已经取消。");
                }
    
                return (ServiceReference1.Empno)cs._Result;
    
            }
            /// 
            /// 这是线程启动函数.
            /// 
            /// 
            static void ThreadGetEmpInfo(object o)
            {
                // 得到传入的参数.
                CallState cs = o as CallState;
                // 注册调用完成的事件响应函数
                cs._proxy.GetEmployeeInfoCompleted += 
                    new EventHandler
                        (proxy_GetEmployeeInfoCompleted);
                // 异步调用. 这个服务只是输入一个操作员工号, 输出一个姓名.
                cs._proxy.GetEmployeeInfoAsync(cs._oParam as string, cs);
            }
    
            /// 
            /// 异步调用完成响应
            /// 
            /// 
            /// 
            static void proxy_GetEmployeeInfoCompleted(object sender, 
                SilverlightApplication2.ServiceReference1.GetEmployeeInfoCompletedEventArgs e)
            {
                // 得到传入的调用上下文参数对象.
                CallState cs = e.UserState as CallState;
    
                // 取消事件委托
                cs._proxy.GetEmployeeInfoCompleted -= 
                    new EventHandler
                        (proxy_GetEmployeeInfoCompleted);
                
                // 收获结果。通过传入的上下文参数对象回应结果或是错误。
                cs._Error = e.Error;
                cs._Result = e.Result;
                cs._Cancelled = e.Cancelled;
               
                Thread.Sleep(5000); // 这里模拟长时间不返回。
    
                // 设置信号量,以便于主线程序的waitone得到信号而继续执行。
                cs._AutoResetEvent.Set();
            }
        }
    
    }
    
    

    HAL
    2009年4月10日 4:31

答案

  • 关键字   io  强行  异步。。。
    没有io  随便
    工作突然有点忙 嘿嘿
    2009年5月4日 4:31
  • WebClient WebRequest WebService WCF皆不支持并发 I/O 操作。也就是全局的这些异步io的方法都不适宜多线程去操作一个。如要操作就要对特定方法添加flag。

    线程中动态生成不只是WebService,经测试以上这些也都会出现线程阻塞即使不进行Thread.Sleep也会阻塞。可见线程中不能再动态生成方式进行异步调用。

    如果一定要在线程中调用,我建议还是在主线程中生成好全局的方法添加flag标识。在线程内再进行控制。

    有些拗口> .<,


            private void ThreadProc(object o)
            {
                AutoResetEvent evt = o as AutoResetEvent;
                for (int i = 0; i < 5; i++)
                {
                    System.Diagnostics.Debug.WriteLine("time:{0:HH:mm:ss:ffff} thid:{1} looped. i={2}",
                        DateTime.Now, System.Threading.Thread.CurrentThread.ManagedThreadId, i);
                    //System.Threading.Thread.Sleep(5000);
                    //ThreadSleep5000();
                    wc5000 = new WebClient();
                    wc5000.OpenReadCompleted += new OpenReadCompletedEventHandler(wc5000_OpenReadCompleted);
                    wc5000.OpenReadAsync(myuri);
                }

                // 设置信号灯。
                if (evt != null)
                {
                    evt.Set();
                }
            }

    验明正身,如上方法确实会造成线程阻塞.不要在线程内动态生成了

            WebClient wc2000;
            Uri myuri;
            public MainPage()
            {
                InitializeComponent();
                wc2000 = new WebClient();
                wc2000.OpenReadCompleted += new OpenReadCompletedEventHandler(wc2000_OpenReadCompleted);
                myuri = new Uri("test0.gif",UriKind.Relative);
            }
    
            bool canbegin = true;
            void wc2000_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
            {
                canbegin = false;
            }
    
            private void ThreadProc(object o)
            {            
                wc2000.OpenReadAsync(myuri);
                while (canbegin)
                {
                    System.Threading.Thread.Sleep(30);
                }
                canbegin = true;
                //线程中异步读取成功并转换为同步操作,可以开始读取全局变量
            }


    建议摒弃原有思路用如上方法就可简单解决异步调用的同步需求

    2009年5月4日 5:56
    版主
  • 最后来一个结论吧!

    我现在开始考虑,执着地在SL里实现WS的同步调用,是不是非常有必要?

    最初在WS里实现在同步调用,其原因是这样一个场景:为了实现一个业务逻辑,需要调用两个WS,并且用这两次调用的结果来调用第三个,这导致了应用程序的复杂度大大增加,因此才导致了这次锲而不舍地探索。

    但是最后我发现,可能是我自己错了,可能是我把WS的颗粒度切得太小了。比如说,我需要一个服务器端的功能时,要设计一个WS方法,但是出于习惯,我会考虑这个WS方法有没有可能被复用,最后发现这个操作是两个更原子操作的组合,于是我就写了两个WS方法出来,希望可以在其他场合来重用这些方法。

    这样,组合原子WS方法的逻辑就只好在客户端的代码完成。因此我就需要在SL的一个方法中实现多个WS的调用。这造成了很大麻烦,于是我开始考虑如何实现同步WS调用……

    结论就是:我可能搞错了方法论。

    在RIA应用中,是否还应该用传统建模的方式来考虑问题呢?在前面所说的应用场景中,把WS分拆为更原子性的操作以提高复用度的设计方法,是否合适呢?如果我们考虑是的是企业SOA目录总线的话,是应该进行这样的分析,但是现在我们面向的只是一个单一的应用,甚至在默认情况下,连跨域的服务都没办法调用!

    因此,在面向SL应用时,我们应该把架构师的心态放到一边去,把眼光收得小一些,不要指望专门为特定SL程序服务的WS还能给其他应用复用,而是应该如同ajax的服务设计一样,踏踏实实地着眼于特定的业务逻辑,把服务的颗粒度与业务操作的颗粒度对齐,需要什么样的操作,就去写什么样的服务。

    这就是所谓的“反模式”设计吧!


    最后,还要指出,反模式设计与WS的复用性设计似乎也可以得到统一——我们还是可以对WS的操作进行一个原子事务级颗粒度的切分,分别提供服务,然后,为SL应用提供一个与前台操作对应的粗颗粒度的WS方法,粗颗粒度的WS方法仍然可以调用原子事务方法来组装功能。


    HAL
    2009年5月19日 7:11

全部回复

  • check如下几点:

    • Server端程序是否被Hit了。
    • 你的启动project是不是你的WebProject? 否则会有cross-domain的问题。
    • Silverlight运行的是不是在Http://? 下?确认不是file://. 否则会有Cross-schema的问题。

    装个Fiddler2 检测一下request 和 response

    2009年4月10日 18:39
  • 我怀疑是       cs._AutoResetEvent.Set();之后cs._AutoResetEvent.WaitOne();没有被继续执行,
    你用断点调试一下看看程序走到那步了

    My blog: http://blog.csdn.net/dotfun http://dotfun.cnblogs.com

    My contact: QQ:372900288 E-mail:372900288@qq.com msn:sellnet007@hotmail.com

    2009年4月11日 15:42
    版主
  • 另外,我感觉你这个程序用异步也可以,为什么一定要搞个多线程的同步吗,呵呵

    My blog: http://blog.csdn.net/dotfun http://dotfun.cnblogs.com

    My contact: QQ:372900288 E-mail:372900288@qq.com msn:sellnet007@hotmail.com

    2009年4月11日 15:49
    版主
  • 哦。我在贴子里已经说明了。如果我不写waitone()那一句,就调用WS正常,只不过不能实现同步调用。如果写下了waitone,调用的complate事件就永远不会被触发了。



    因此,很明显, 问题没有出在WS的调用权限方面,而是在信号量的同步上。

    HAL
    2009年4月13日 2:27
  • 不。程序必须采用同步调用。因为业务逻辑是像这样的:

    收集界面的数据,调用一个WSa,得到结果,继续调用另一个WSb,得到结果,加工这些数据,调用第三个WSc。。。。。

    LOB程序多是这样的逻辑,如果用异步调用,这个很流畅的业务逻辑就被搞得乱七八糟,如:

    界面事件处理:   收集界面数据。注册WSa的完成回调函数,调用WSa,退出。
    WSa完成回调函数: 得到结果,加工数据,注册WSb的完成回调函数。调用WSb,退出。
    WSb完成回调函数:得到结果,加工数据,注册WSc的完成回调函数。调用WSc,退出。
    WSbc成回调函数:得到结果,加工数据,退出。

    如果考虑到在等待期间对界面的封锁以防止用户乱改输入的内容、某操作出错后的回滚等等,那真得是没办法处理了。

    因此,必须想办法解决同步调用,以简化LOB程序的开发。


    HAL
    2009年4月13日 2:36
  • 不是。这个我调试过了。在cs._AutoResetEvent.WaitOne()之后,那个proxy_GetEmployeeInfoCompleted()就从来没有被触发过,永远地等了下去。

    HAL
    2009年4月13日 2:46
  • 把最新的实验结果给大家看一下:

    我在程序各处加上debug的输出,然后请看多线程运行的结果:

    0: 11:33:34.7955 thid:1 Prepare to start new Thread!                 // 准备启动新线程来调用WS
    1: 11:33:34.8875 thid:1 ThreadStart!                                        // 立即就启动了。
    2: 11:33:34.8925 thid:1 sleep 5 seconds, for WS Calling can long Execute.   // 主程序中我写了个sleep(5000), 是想看看主线程休眠后,子线程能不能执行。
    3: 11:33:37.8675 thid:4 ThreadEnter!                // 这是子线程里的第一句程序。这时已经过去了3秒种了。
    4: 11:33:39.8935 thid:1 sleep end.                   // 主线程结束了5秒的休眠。奇怪的是,在子线程的第一句执行完之后,到主线程序醒来之间的2秒钟里,子线程并没有继续向下执行!!!为什么????
    5: 11:33:39.8945 thid:1 StartWait!               // 主线程开始用信号量等待了。这次,我设置5秒超时。
    6: 11:33:44.8955 thid:1 EndWait, 超时!         // 主线程等到了超时,信号也没有送到。 注意,这5秒里,子线程什么也没干,也停在那里了。
    7: 11:33:44.8965 thid:1 GetResult!            // 主线程试着看看有没有结果。结果是空的,因为子线程什么也没干呢。这之后,主线程就退出了。
    8: 11:33:45.2867 thid:4 CallWsStart!         // 子线程终于开始动了。它开始调用webservice.
    线程 0x53ac 已退出,返回值为 0 (0x0)。    // 子线程调完,函数结束,子线程退出。
    “iexplore.exe”(Silverlight): 已加载“c:\Program Files\Microsoft Silverlight\2.0.40115.0\en-US\System.Runtime.Serialization.debug.resources.dll”
    “iexplore.exe”(Silverlight): 已加载“c:\Program Files\Microsoft Silverlight\2.0.40115.0\zh-Hans\System.Runtime.Serialization.debug.resources.dll”
    9: 11:33:45.8007 thid:6 CallWsComplated!   // 系统又启动了一个新的线程来接callwscomplate事件。这个线程的ID是6.
    10: 11:33:45.8007 thid:6 sleep 5 seconds to simulate long time calling!  // 我在事件的响应里,写了个等5秒来模拟长时间的WS调用。
    11: 11:33:50.8083 thid:6 sleep end.      // 休眠结束。

    从上面的过程来看,在主线程sleep\wait信号量的情况下,子线程也停下来等待。不知道这是什么原因。

    这种现象在winform程序中时是不存在的。主线程序可以停下来等待子线程的信号,若干子线程执行到结束后,主线程就得到信号,没有一点问题。

    另外,我用一般的方法来调用WS,结果发现按钮的click事件函数和wscallingcomplate事件的响应函数,在执行时,都处于1号线程中,而不是像上面的多线程方式一样,会在一个新的线程里执行。这就是为什么可以在complate事件里直接修改界面上的元素属性。

    看来,SL的多线程体系和winform的有巨大的不同。考虑到SL面向的是跨平台,极有可能它的多线程是用同一个浏览器线程模拟出来的,由于在处理阻塞时有一些问题,会导致在一些情况下无法将时间片移交到其他线程上。

    之后,我会再继续试验。并把结果记录在这里。


    HAL
    2009年4月14日 3:53
  • 来的人好少。

    看来对silverlight技术中稍为深入的知识,熟悉的人还是不多呀!


    HAL
    2009年4月17日 3:09
  • 上次我去 .net技术大会  那边的讲师说  sl的所有io操作都是强制异步的   不知道说得是不是这个情况


    工作突然有点忙 嘿嘿
    2009年4月17日 3:13
  • 还是建议你先Debug一下Server端,看看Webservice 是否真正的被Call并且返回了。
    2009年4月17日 20:22
  • 还是建议你先Debug一下Server端,看看Webservice 是否真正的被Call并且返回了。

    我上面说得难道还不明白吗?

    现在问题已经确定到:一但我开始wait信号灯,webservice的调用就根本不会发生了。
    HAL
    2009年4月18日 6:57
  • 不好意思.是我理解不够.
    那你有没有试过新建一个BackGround Thread 然后从这个Background thread调用WebService呢? 因为从你的描述看来似乎是UI Thread已经被block 住了.
    2009年4月19日 5:40
  • 对于楼主特定的问题,我觉得不必要将其放在特定的线程中,
    参看下面伪代码,
    void demo()
    {
     ws1read.Async();
    }
    ws1_ReadCompleted()
    {
     ws2read.Async();
    }
    ws2_ReadCompleted()
    {
     ws3read.Async();
    }

    在一个方法Completed后再去Async其他方法是没问题的,而且这样也方便检查错误跟踪事件
    2009年4月22日 15:48
    版主
  • 是这样的:实际的业务逻辑远比上面的例子复杂,上面只是为了简化问题而设计的一个场景。我实际上解决的,是这一类问题。

    无论如何,如果能实现WS的同步调用,都会让程序的逻辑简化很多,对业务生产线程序的开发的效率有极大提高。

    如果用上面的方法异步处理,比较头疼的问题有:

    同一个逻辑体被分为多个技术上的执行段,各个段之间如何传递上下文数据?
           有两个办法,一是用页面对象的成员变量,这会导致与其他业务逻辑纠缠不清。
           二是采用调用时的userstat参数来传递上下文,这个方法好一些,但是要定义好多新的临时类来包装上下文。

    考虑这样一种情况:同一个WS的同一个方法,在同一个SL程序中的多处的逻辑中被多次调用(这是业务生产程序中很常见的情况),我们就不得不在不同的调用中,为相同的WS调用complate事件委托不同的后续处理方法。可以想象,这个程序的复杂度会随着业务逻辑的复杂度而平方数上升。

    我试着用SL做了几个业务处理程序,很快发现当程序逻辑与服务器之间的交互多起来之后,程序快速地变得非常复杂。


    HAL
    2009年4月27日 2:02
  • Silverlight是异步处理的程序,不支持同步操作。
    楼主可以看看Silverlight 3 Beta SDK开源的Children Window 控件,在执行一个ws时为获取返回值,锁定主场景,当获取了这个返回值或者报错时再进行相应的处理。这样解决业务逻辑上需求应该方便的多
    2009年4月27日 3:37
    版主
  • Silverlight是异步处理的程序,不支持同步操作。
    楼主可以看看Silverlight 3 Beta SDK开源的Children Window 控件,在执行一个ws时为获取返回值,锁定主场景,当获取了这个返回值或者报错时再进行相应的处理。这样解决业务逻辑上需求应该方便的多

    上次我去 .net技术大会  那边的讲师说  sl的所有io操作都是强制异步的   不知道说得是不是这个情况


    工作突然有点忙 嘿嘿

    似乎就是这样

    工作突然有点忙 嘿嘿
    2009年4月27日 4:35
  • 我这几天抽空测试一次silverlight里的多线程问题。也一样用了信号量等待机制,子线程休眠,主线程休眠等,只是没有去调用webservice,奇怪的是,什么问题也没有,一切正常!线程都同步运行了!

    郁闷!难道是在调用webservice时,有什么内部的锁,或是临界段的设置?

    下面是代码:

    // 这是按钮事件处理
    private void btTestThread_Click(object sender, RoutedEventArgs e)
    {
        
        System.Diagnostics.Debug.WriteLine("time:{0:HH:mm:ss:ffff} thid:{1} start create thread.",
                DateTime.Now, System.Threading.Thread.CurrentThread.ManagedThreadId);
        // 做5个事件信号,之后分给每一个线程。
        AutoResetEvent[] arEvt = new AutoResetEvent[5];
    
        // 每隔2秒创建1个线程,共5个线程。每个线程给一个信号灯。线程结束时,就设置信号,主程序会等所有的信号都已经设置后,才会继续运行。
        for (int i = 0; i < 5; i++)
        {
            System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(ThreadProc));
            arEvt[i] = new AutoResetEvent(false);
            th.Start(arEvt[i]);
    
            System.Diagnostics.Debug.WriteLine("time:{0:HH:mm:ss:ffff} thread No.{1} started.",
                DateTime.Now, i);
            System.Threading.Thread.Sleep(2000);
    
        }
        System.Diagnostics.Debug.WriteLine("time:{0:HH:mm:ss:ffff} thid:{1} Wait All Finish.",
            DateTime.Now, System.Threading.Thread.CurrentThread.ManagedThreadId);
    
        // 等所有的信号被设置。
        AutoResetEvent.WaitAll(arEvt);
    
    
        System.Diagnostics.Debug.WriteLine("time:{0:HH:mm:ss:ffff} thid:{1} all thread is end.",
            DateTime.Now, System.Threading.Thread.CurrentThread.ManagedThreadId);
    }
    // 线程方法。只是循环5次,每次报告是第几次循环,并休眠5秒。
    private void ThreadProc(object o)
    {
        AutoResetEvent evt = o as AutoResetEvent;
        for (int i = 0; i < 5; i++)
        {
            System.Diagnostics.Debug.WriteLine("time:{0:HH:mm:ss:ffff} thid:{1} looped. i={2}", 
                DateTime.Now, System.Threading.Thread.CurrentThread.ManagedThreadId, i);
            System.Threading.Thread.Sleep(5000);
        }
    
        // 设置信号灯。
        if (evt != null)
        {
            evt.Set();
        }
    }
    


    下面是运行时DEBUG窗口的消息输出:

    下面的结果表示,主线程创建了5个线程,线程ID分别为:
    4,5,6,7,8
    time:12:13:59:8501 thid:1 start create thread.     // 按钮按下,主函数开始运行   
    time:12:14:00:0361 thid:4 looped. i=0              // 4号线程已经启动了。
    time:12:14:00:0371 thread No.0 started.            // 主线程汇报第0个启动了,其实也是指的第4号。
    time:12:14:02:0421 thread No.1 started.            // 主线程汇报第1个启动了,其实指5号线程。以下不再说明了。
    time:12:14:02:0451 thid:5 looped. i=0              //
    time:12:14:04:0541 thid:6 looped. i=0              //
    time:12:14:04:0521 thread No.2 started.            //
    time:12:14:05:0371 thid:4 looped. i=1              //
    time:12:14:06:0561 thread No.3 started.            //
    time:12:14:06:0701 thid:7 looped. i=0              //
    time:12:14:07:0501 thid:5 looped. i=1              //
    time:12:14:08:0681 thread No.4 started.            //
    time:12:14:08:0781 thid:8 looped. i=0              //
    time:12:14:09:0561 thid:6 looped. i=1              //
    time:12:14:10:0381 thid:4 looped. i=2              //
    time:12:14:10:0711 thid:1 Wait All Finish.         // 5个线程启动完毕,主线程开始等待全部信号灯。
    time:12:14:11:0721 thid:7 looped. i=1              //
    time:12:14:12:0541 thid:5 looped. i=2              //
    time:12:14:13:0831 thid:8 looped. i=1              //
    time:12:14:14:0581 thid:6 looped. i=2              //
    time:12:14:15:0391 thid:4 looped. i=3              //
    time:12:14:16:0741 thid:7 looped. i=2              //
    time:12:14:17:0551 thid:5 looped. i=3              //
    time:12:14:18:0871 thid:8 looped. i=2              //
    time:12:14:19:0691 thid:6 looped. i=3              //
    time:12:14:20:0401 thid:4 looped. i=4              //
    time:12:14:21:0751 thid:7 looped. i=3              //
    time:12:14:22:0561 thid:5 looped. i=4              //
    time:12:14:23:1011 thid:8 looped. i=3              //
    time:12:14:24:0791 thid:6 looped. i=4              //
    线程 0x1470 已退出,返回值为 0 (0x0)。             // 子线程纷纷完成,退出。
    time:12:14:26:0771 thid:7 looped. i=4              //
    线程 0x1480 已退出,返回值为 0 (0x0)。             //
    time:12:14:28:1171 thid:8 looped. i=4              //
    线程 0x1484 已退出,返回值为 0 (0x0)。             //
    线程 0x117c 已退出,返回值为 0 (0x0)。             //
    线程 0x17c4 已退出,返回值为 0 (0x0)。             //
    time:12:14:33:1281 thid:1 all thread is end.       // 主线程等到了全部信号。

    看看时间上序列:

    主线程的汇报:
    time:12:14:00:0371 thread No.0 started.  
    time:12:14:02:0421 thread No.1 started.  
    time:12:14:04:0521 thread No.2 started.
    time:12:14:06:0561 thread No.3 started.
    time:12:14:08:0681 thread No.4 started.   
    time:12:14:10:0711 thid:1 Wait All Finish.
    time:12:14:33:1281 thid:1 all thread is end.

    主线程每2秒启动一个子线程,没有受到子线程的休眠影响。

    子线程的汇报:按子线程分了一下。
    time:12:14:00:0361 thid:4 looped. i=0
    time:12:14:05:0371 thid:4 looped. i=1
    time:12:14:10:0381 thid:4 looped. i=2
    time:12:14:15:0391 thid:4 looped. i=3
    time:12:14:20:0401 thid:4 looped. i=4

    time:12:14:02:0451 thid:5 looped. i=0
    time:12:14:07:0501 thid:5 looped. i=1
    time:12:14:12:0541 thid:5 looped. i=2
    time:12:14:17:0551 thid:5 looped. i=3
    time:12:14:22:0561 thid:5 looped. i=4

    time:12:14:04:0541 thid:6 looped. i=0
    time:12:14:09:0561 thid:6 looped. i=1
    time:12:14:14:0581 thid:6 looped. i=2
    time:12:14:19:0691 thid:6 looped. i=3
    time:12:14:24:0791 thid:6 looped. i=4

    time:12:14:06:0701 thid:7 looped. i=0
    time:12:14:11:0721 thid:7 looped. i=1
    time:12:14:16:0741 thid:7 looped. i=2
    time:12:14:21:0751 thid:7 looped. i=3
    time:12:14:26:0771 thid:7 looped. i=4

    time:12:14:08:0781 thid:8 looped. i=0
    time:12:14:13:0831 thid:8 looped. i=1
    time:12:14:18:0871 thid:8 looped. i=2
    time:12:14:23:1011 thid:8 looped. i=3
    time:12:14:28:1171 thid:8 looped. i=4

    子线程也没有受到主程序休眠的影响。

    结论:常规的多线程没有问题。

    为什么在webservice调用时,就发生相互阻塞的情况呢?

    生气!!


    HAL
    2009年4月30日 4:30
  • 关键字   io  强行  异步。。。
    没有io  随便
    工作突然有点忙 嘿嘿
    2009年5月4日 4:31
  • WebClient WebRequest WebService WCF皆不支持并发 I/O 操作。也就是全局的这些异步io的方法都不适宜多线程去操作一个。如要操作就要对特定方法添加flag。

    线程中动态生成不只是WebService,经测试以上这些也都会出现线程阻塞即使不进行Thread.Sleep也会阻塞。可见线程中不能再动态生成方式进行异步调用。

    如果一定要在线程中调用,我建议还是在主线程中生成好全局的方法添加flag标识。在线程内再进行控制。

    有些拗口> .<,


            private void ThreadProc(object o)
            {
                AutoResetEvent evt = o as AutoResetEvent;
                for (int i = 0; i < 5; i++)
                {
                    System.Diagnostics.Debug.WriteLine("time:{0:HH:mm:ss:ffff} thid:{1} looped. i={2}",
                        DateTime.Now, System.Threading.Thread.CurrentThread.ManagedThreadId, i);
                    //System.Threading.Thread.Sleep(5000);
                    //ThreadSleep5000();
                    wc5000 = new WebClient();
                    wc5000.OpenReadCompleted += new OpenReadCompletedEventHandler(wc5000_OpenReadCompleted);
                    wc5000.OpenReadAsync(myuri);
                }

                // 设置信号灯。
                if (evt != null)
                {
                    evt.Set();
                }
            }

    验明正身,如上方法确实会造成线程阻塞.不要在线程内动态生成了

            WebClient wc2000;
            Uri myuri;
            public MainPage()
            {
                InitializeComponent();
                wc2000 = new WebClient();
                wc2000.OpenReadCompleted += new OpenReadCompletedEventHandler(wc2000_OpenReadCompleted);
                myuri = new Uri("test0.gif",UriKind.Relative);
            }
    
            bool canbegin = true;
            void wc2000_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
            {
                canbegin = false;
            }
    
            private void ThreadProc(object o)
            {            
                wc2000.OpenReadAsync(myuri);
                while (canbegin)
                {
                    System.Threading.Thread.Sleep(30);
                }
                canbegin = true;
                //线程中异步读取成功并转换为同步操作,可以开始读取全局变量
            }


    建议摒弃原有思路用如上方法就可简单解决异步调用的同步需求

    2009年5月4日 5:56
    版主
  • 关键字   io  强行  异步。。。
    没有io  随便
    工作突然有点忙 嘿嘿

    难道就是因为这个?

    这这这~!@#¥%……&*(*(&……%¥#@#@@~#@!~


    HAL
    2009年5月4日 5:56
  • 难道就是因为这个?

    这这这~!@#¥%……&*(*(&……%¥#@#@@~#@!~


    HAL
    ......

    既然是异步为核心的RIA程序,在数据读取上就应该有自己全新的架构了,直接套用以前的架构或方法不适应也应该可以理解啦:P
    2009年5月4日 6:13
    版主
  • 最后来一个结论吧!

    我现在开始考虑,执着地在SL里实现WS的同步调用,是不是非常有必要?

    最初在WS里实现在同步调用,其原因是这样一个场景:为了实现一个业务逻辑,需要调用两个WS,并且用这两次调用的结果来调用第三个,这导致了应用程序的复杂度大大增加,因此才导致了这次锲而不舍地探索。

    但是最后我发现,可能是我自己错了,可能是我把WS的颗粒度切得太小了。比如说,我需要一个服务器端的功能时,要设计一个WS方法,但是出于习惯,我会考虑这个WS方法有没有可能被复用,最后发现这个操作是两个更原子操作的组合,于是我就写了两个WS方法出来,希望可以在其他场合来重用这些方法。

    这样,组合原子WS方法的逻辑就只好在客户端的代码完成。因此我就需要在SL的一个方法中实现多个WS的调用。这造成了很大麻烦,于是我开始考虑如何实现同步WS调用……

    结论就是:我可能搞错了方法论。

    在RIA应用中,是否还应该用传统建模的方式来考虑问题呢?在前面所说的应用场景中,把WS分拆为更原子性的操作以提高复用度的设计方法,是否合适呢?如果我们考虑是的是企业SOA目录总线的话,是应该进行这样的分析,但是现在我们面向的只是一个单一的应用,甚至在默认情况下,连跨域的服务都没办法调用!

    因此,在面向SL应用时,我们应该把架构师的心态放到一边去,把眼光收得小一些,不要指望专门为特定SL程序服务的WS还能给其他应用复用,而是应该如同ajax的服务设计一样,踏踏实实地着眼于特定的业务逻辑,把服务的颗粒度与业务操作的颗粒度对齐,需要什么样的操作,就去写什么样的服务。

    这就是所谓的“反模式”设计吧!


    最后,还要指出,反模式设计与WS的复用性设计似乎也可以得到统一——我们还是可以对WS的操作进行一个原子事务级颗粒度的切分,分别提供服务,然后,为SL应用提供一个与前台操作对应的粗颗粒度的WS方法,粗颗粒度的WS方法仍然可以调用原子事务方法来组装功能。


    HAL
    2009年5月19日 7:11
  • 赞:),楼主思索很是深刻,确实如此,原有的开发模式强硬的应用到现有的RIA开发中确会造成一些困扰,只能用于参考,Ajax的模式和Jquery都可以参考

    2009年5月19日 9:13
    版主
  • 据本人试验,在UI线程中使用这种同步方式是不行的,但是,我在按钮的事件中启动另外一个线程,在这个线程中执行即能成功执行异步操作的同步封装。
    但是,如果在这个线程中需要依照结果来更新UI,那就需要想另外的办法了。比如写代理函数通过Dispatcher来更新UI之类的。
    2009年7月2日 3:08