使用ThreadPool发起同步的调用

让我们来思考一下这个问题,它的本质是,如果你采用异步调用,则无法同时发起多个请求(对同一个proxy而言)。那么如果是同步调用的话,就自然没有这个问题了吧。但是,如果同步调用的话,却又会阻塞线程,导致你根本无法发起第二个请求。

这有何难呢?我们大不了可以开两个线程,然后让这两个线程同时工作,在它们上面运行各自的同步调用方法,不就可以了么?为了让大家更好地理解,我用最简单的做法来实现吧

ThreadPool是一个静态类,顾名思义,它代表了线程池的概念。对于初学者而言,创建和控制线程并非易事。所以,.NET提供了这个ThreadPool,实际上相当于是傻瓜式的多线程(多任务)的接口。

csharpcode 复制代码
<span style="color:#000000"><span style="background-color:#ffffff"><span style="color:#0000ff">using</span> System;
<span style="color:#0000ff">using</span> System.Windows.Forms;
<span style="color:#0000ff">using</span> System.Threading;

<span style="color:#0000ff">namespace</span> WindowsFormsApplication1
{
    <span style="color:#0000ff">public</span> <span style="color:#0000ff">partial</span> <span style="color:#0000ff">class</span> Form1 : Form
    {
        <span style="color:#0000ff">public</span> Form1()
        {
            InitializeComponent();
        }


        localhost.WebService1 proxy = <span style="color:#0000ff">new</span> localhost.WebService1();

        <span style="color:#0000ff">private</span> <span style="color:#0000ff">void</span> button1_Click(<span style="color:#0000ff">object</span> sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem(
                (obj) =>
                {
                    var result = proxy.HelloWorld();
                    MessageBox.Show(result);
                });



        }

        <span style="color:#0000ff">private</span> <span style="color:#0000ff">void</span> button2_Click(<span style="color:#0000ff">object</span> sender, EventArgs e)
        {

            ThreadPool.QueueUserWorkItem(
                (obj) =>
                {
                    var result = proxy.HelloWorld();
                    MessageBox.Show(result);
                });


        }
    }
}
</span></span>

ThreadPool类型有一个静态方法,QueueUserWorkItem,顾名思义就是将用户任务排进队列,等待执行。你可能会说,那么这是要排队么?当然,但是既然称为Pool,也即是池,就自然不止一个线程,它里面有好多个线程可以使用,自然就可以同时运行多个任务了。关于这一点,大家可以类比一下我们在超市买完东西之后,去收银台付费的情况。(很多大超市都有声势浩大的一排收银台)

【注意】要实现多线程还有很多做法,例如下面这样无疑也是可以的

csharpcode 复制代码
<span style="color:#000000"><span style="background-color:#ffffff"><span style="color:#0000ff">using</span> System;
<span style="color:#0000ff">using</span> System.Windows.Forms;
<span style="color:#0000ff">using</span> System.Threading;

<span style="color:#0000ff">namespace</span> WindowsFormsApplication1
{
    <span style="color:#0000ff">public</span> <span style="color:#0000ff">partial</span> <span style="color:#0000ff">class</span> Form1 : Form
    {
        <span style="color:#0000ff">public</span> Form1()
        {
            InitializeComponent();
        }


        localhost.WebService1 proxy = <span style="color:#0000ff">new</span> localhost.WebService1();

        <span style="color:#0000ff">private</span> <span style="color:#0000ff">void</span> button1_Click(<span style="color:#0000ff">object</span> sender, EventArgs e)
        {

            (<span style="color:#0000ff">new</span> Thread((obj) =>
            {
                var result = proxy.HelloWorld();
                MessageBox.Show(result);
            })).Start();

        }

        <span style="color:#0000ff">private</span> <span style="color:#0000ff">void</span> button2_Click(<span style="color:#0000ff">object</span> sender, EventArgs e)
        {

            (<span style="color:#0000ff">new</span> Thread((obj) =>
            {
                var result = proxy.HelloWorld();
                MessageBox.Show(result);
            })).Start();
        }
    }
}
</span></span>

这个做法,是由我们自己创建并启动了一个线程。这看起来虽然比较刺激,但并见得是多么好的一个做法。尤其是你对线程操作并不是特别熟悉的情况下,我推荐你还是多用ThreadPool

至于,其他的做法(如Backgroundworker,以及自定义Callback等),这里就不列举了。

4. 线程安全问题

这样做,看起来是解决了问题了。但是大家要知道,通常情况下,我们希望调用Web Service获得的结果,能进一步地进行处理,或者要显示在界面上。例如下面这样

csharpcode 复制代码
<span style="color:#000000"><span style="background-color:#ffffff"><span style="color:#0000ff">using</span> System;
<span style="color:#0000ff">using</span> System.Windows.Forms;
<span style="color:#0000ff">using</span> System.Threading;

<span style="color:#0000ff">namespace</span> WindowsFormsApplication1
{
    <span style="color:#0000ff">public</span> <span style="color:#0000ff">partial</span> <span style="color:#0000ff">class</span> Form1 : Form
    {
        <span style="color:#0000ff">public</span> Form1()
        {
            InitializeComponent();
        }


        localhost.WebService1 proxy = <span style="color:#0000ff">new</span> localhost.WebService1();

        <span style="color:#0000ff">private</span> <span style="color:#0000ff">void</span> button1_Click(<span style="color:#0000ff">object</span> sender, EventArgs e)
        {

            ThreadPool.QueueUserWorkItem(
                (obj) =>
                {
                    var result = proxy.HelloWorld();
                    <strong><span style="color:#0000ff">this</span>.Text = result;<span style="color:#008000">//将结果显示在窗体的标题上</span></strong>
                });

        }

        <span style="color:#0000ff">private</span> <span style="color:#0000ff">void</span> button2_Click(<span style="color:#0000ff">object</span> sender, EventArgs e)
        {

            ThreadPool.QueueUserWorkItem(
                (obj) =>
                {
                    var result = proxy.HelloWorld();
                    <strong><span style="color:#ff0000"><span style="color:#0000ff">this</span>.Text = result;</span></strong>
                });
        }
    }
}
</span></span>

再次运行这个代码就会发现,它又报错了,这回报告的错误是下面这样的

这个错误的意思是说,当前的线程需要访问控件"Form1",但是Form1并不是它所创建的。什么是当前线程?就是我们用ThreadPool发起的一个任务所占用的线程。而Form1这个控件是谁创建的呢?是主线程创建的。

这就是我们常说的线程安全性问题,也就是说,在多个不同的线程之间是不能互相访问到一些资源的。还是用超市的收银做例子,如果我是排在1号收银台,那么2号收银员是不可以把我的钱拿走的。

那么,如何解决这个问题呢?其实这还是比较简单的,Windows Forms(或者WPF)都内置了一些机制来解决

csharpcode 复制代码
<span style="color:#000000"><span style="background-color:#ffffff"><span style="color:#0000ff">using</span> System;
<span style="color:#0000ff">using</span> System.Windows.Forms;
<span style="color:#0000ff">using</span> System.Threading;</span></span>
相关推荐
2603_965148116 小时前
如何解析JSON数据?API返回的商品信息处理教程
开发语言·数据库·python·自动化·json·api
ltl7 小时前
RocksDB 经典故障排查:L0、compaction 与 write stall
数据库
xfhuangfu8 小时前
Oracle中建立到CDB和PDB的连接
数据库·oracle·rpc
小小龙学IT8 小时前
DuckDB 深度实战:用 C++ 在进程内跑一个「分析型数据库」
数据库·c++
NineData8 小时前
DTCC 2026 预告|NineData CEO& 创始人叶正盛:面向 AI Agent 的数据库 DevOps 与数据复制实践
数据库·人工智能·数据库开发·devops·ninedata·数据库技术·dtcc
J_bean9 小时前
MySQL 事务是否必须手动开启?
数据库·mysql·数据库事务·自动提交·手动提交
J_bean9 小时前
MySQL InnoDB 如何检测死锁、判定死锁、处理死锁
数据库·mysql·死锁·死锁检测·处理死锁·死锁判定
浪子明X9 小时前
从 MongoDB 文档到关系模型:构建可重跑、可对账的数据迁移流水线
数据库·mongodb·oracle
杜子不疼.9 小时前
国产数据库撑起固井软件自主化:金仓 × 中海油服「海恒 Cemsol」落地解析
数据库
隔窗听雨眠9 小时前
GBase 8s并发控制深度解析:封锁机制、隔离级别与死锁处理全攻略
服务器·数据库·oracle