目录
并发和并行:... 1
parallel,并行:... 1
concurrency,并发:... 2
并发的解决:... 2
1、队列、缓冲区:... 2
2、争抢:... 3
3、预处理:... 3
4、并行:... 3
5、提速:... 3
6、消息中间件:... 3
进程和线程:... 4
线程的状态:... 5
py的线程开发:... 5
threading.Thread类:... 6
线程启动:... 6
线程退出:... 7
线程的传参:... 8
threading的属性和方法:... 8
Thread实例的属性和方法:... 9
t.start()和t.run():... 9
线程安全:... 11
daemon和non-daemon:... 11
t.join() 14
threading.local类:... 15
threading.Timer类:... 18
同时做某些事,可互不干扰的同一时刻做几件事;
例:
高速公路的车道,所有车辆(数据)可互不干扰的在自己的车道上奔跑(传输);在同一时刻,每条车道上可能同时有车辆在跑,是同时发生的概念;
在一定程度上,并行可解决并发问题;并行并不是解决并发的最佳方案;
也是同时做某些事,但是强调,同一时段做了几件事;
例:
乡村公路一条车道,半幅路面出现了坑,交警指挥车辆走另外半幅路面,一个方向放行3min,换另一个方向,发生了车辆同时要通过路面的事件,这就是并发;
并发是在一时间点有很多事要做,并发太大,如何做?怎么做并不知道;
队列、缓冲区:
乡村公路窄,车辆就会排队,所以排队是一种天然解决并发的办法,车辆排队的缓冲地带就是缓冲区;
并行:
乡村公路窄,拓宽成双向四车道,可同时双向车道通行,通行车辆增多,能满足一定的通行需求;
食堂打饭模型:
中午12点,开饭,都涌向食堂,这就是并发,如果人很多,就是高并发;
假设只有一个窗口,陆续涌入食堂的,排队打菜是比较好的方式;
排队就是把人排成队列,先进先出,解决了资源使用的问题;
排成的队列,其实就是一个缓冲地带,就是缓冲区;
假设女生优先,那么这个窗口就得是两队,只要有女生就先打饭,男生排队等着,女生队伍就是一个优先队列;
只开一个窗口,有可能没有秩序,也就是谁挤进去就给谁打饭;
挤到窗口的人占据窗口,直到打到饭菜离开;
其他人继续争抢,会有一个人正占据窗口,可以视为锁定窗口,窗口就不能为其他人提供了服务了,这是一种锁机制;
抢到资源就上锁,排他性锁,其他人只能等候;
争抢也是一种高并发解决方案,但不好,因为有可能有人很长时间抢不到;
如果排长队的原因,是由于每个人打菜等候时间长,因为要吃的菜没有,需要现做,没打着饭不走开,锁定着窗口;
可以提前统计大多数最爱吃的菜品,最爱吃的80%的热门菜提前做好,保证供应,20%的冷门菜现做;
这样大多数人,就算锁定窗口,也很快就释放窗口了;
一种提前加载用户需要的数据的思路,预处理思想,缓存常用;
成百上千人同时来吃饭,一个队伍搞不定的,多开打饭窗口形成多个队列,如同开多个车道一样,并行打菜;
开窗口就得扩大食堂,得雇人在每一个窗口提供服务,造成成本上升;
日常可通过购买更多服务器,或多开进程实现并行处理,来解决并发问题;
这些都是水平扩展思想;
提高单个窗口的打饭速度;
打饭人员提高工作技能,或为单个窗口配备更多的服务人员,都是提速的办法;
这是一种垂直扩展思想;
北京地铁站外九曲回肠走廊,缓冲人流,进去之后再多口安检进站;
rabbitmq、activemq(apache)、rocketmq(ali apache)、kafka(apache)等;
以上是最常用的解决方案,一般来说不同的并发场景用不同的策略,而策略可能是多种方式的优化组合;
如,多开食堂(多地),可把食堂建设到宿舍生活区(就近),技术来源于生活;
注:
如果线程在单cpu上处理,就不是并行了;
一般server都是多cpu的,服务的部署往往是多机的、分布式的,这都是并行处理;
真正的好系统,是串行、并行共存的;
串行、并行是解决并发的手段之一,不可厚此薄彼;
串行、并行哪个快?问题本身有问题,类似1车道快还是4车道快;
在实现了线程的OS中,线程是OS能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,一个程序的执行实例就是一个进程;
process,进程:
是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是OS结构的基础;
进程和程序的关系:
程序是源代码编译后的文件,而这些文件存放在disk上,当程序被OS加载到内存中,就是进程,进程中存放着指令和数据(资源),它也是线程的容器;
linux进程有父进程、子进程;
win的进程是平等关系;
thread,线程:
有时被称为light weight process,LWP,轻量级进程,线程是程序执行流的最小单元;
一个标准的线程由线程ID、当前指令指针PC、寄存器集合、堆栈组成;
process、thread理解:
现代OS提出进程的概念,每一个进程都认为自己独占所有的计算机硬件资源;
进程就是独立的王国,进程间不可以随便的共享数据;
进程是国,线程是省;
线程就是省份,同一个进程内的线程可共享进程的资源,每一个线程拥有自己独立的堆栈;
进程是容器,进程内的线程是干活的;
线程是程序执行流的最小单元,基本单元是进程;
py中的进程和线程:
进程会启动一个解释器进程,线程共享一个解释器进程;
ready,就绪态,线程能够运行,但在等待被调度,可能线程刚刚创建启动,或刚刚从阻塞中恢复,或被其它线程抢占;
running,运行态,线程正在运行;
blocked,阻塞态,线程等待外部事件发生而无法运行,如IO操作;
terminated,终止,线程完成、或退出、或被取消;
使用标准库threading;
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)
target,线程调用的对象,就是目标函数;
name,为线程起个名,可重名,区分是靠线程ID;
args,为目标函数传递实参,元组;
kwargs,为目标函数传递关键字参数,字典;
py的线程没有优先级,没有线程组的概念,也不能被销毁、停止、挂起,也就没有恢复、中断了;
例:
import threading
import time
def worker():
for _ in range(5):
time.sleep(1)
print('welcome magedu')
print('thread over')
def worker1():
for _ in range(5):
time.sleep(1)
print('welcome magedu...')
print('thread over...')
t = threading.Thread(target=worker, name='w1') #实例化一个线程对象
t.start() #线程启动,每个线程必须且只能执行该方法1次
# print('~'*30)
# t.start() #RuntimeError: threads can only be started once,t.start()只能一次,如果非要再写一次必须先实例化t=threading.Thread(...)再启动t.start()
t1 = threading.Thread(target=worker1, name='w2') #对于1颗cpu的server,多线程是假并行
t1.start()
输出:
welcome magedu...
welcome magedu
welcome magedu...
welcome magedu
welcome magedu...
welcome magedu
welcome magedu...
welcome magedu
welcome magedu...
welcome magedu
thread over
thread over...
py没有提供线程退出的方法,线程在如下情况时退出:
1、线程函数内语句执行完毕;
2、线程函数中抛出未处理的异常;
例:
import threading
import time
def worker2():
count = 0
while True: #通过在循环中加标记,当某一变量为指定值时退出循环
time.sleep(1)
print('thread quit')
count += 1
if count > 3:
raise Exception('new exception')
t2 = threading.Thread(target=worker2, name='t2')
t2.start()
输出:
thread quit
thread quit
thread quit
thread quit
Exception in thread t2:
Traceback (most recent call last):
File "D:\Python\Python35\lib\threading.py", line 914, in _bootstrap_inner
self.run()
File "D:\Python\Python35\lib\threading.py", line 862, in run
self._target(*self._args, **self._kwargs)
File "E:/git_practice/cmdb/example_threading.py", line 23, in worker2
raise Exception('new exception')
Exception: new exception
本质上就是函数传参,线程传参和函数传参没什么区别;
例:
import threading
import time
def worker3(n):
for _ in range(n):
print('thread arguments')
# t3 = threading.Thread(target=worker3, name='t3', args=(3,))
t3 = threading.Thread(target=worker3, name='t3', kwargs={'n':3})
t3.start()
输出:
thread arguments
thread arguments
thread arguments
current_thread(),返回当前线程对象;
main_thread(),返回主线程对象;
active_count(),当前处于alive状态(一旦被cpu调度才是活动状态ready<->running)的线程个数;
enumerate(),返回所有活着的线程的列表,不包括已终止的线程和未开始的线程;
get_ident(),返回当前线程的ID,非0整数;
例:
def worker(n=3):
print('current_thread: {}'.format(threading.current_thread()))
print('current_thread().name: {}'.format(threading.current_thread().name))
print('currrent_thread().is_alive(): {}'.format(threading.current_thread().is_alive()))
print('main_thread: {}'.format(threading.main_thread()))
print('main_thread().is_alive(): {}'.format(threading.main_thread().is_alive()))
print('active_count: {}'.format(threading.active_count()))
print('enumerate: {}'.format(threading.enumerate()))
for _ in range(n):
time.sleep(0.5)
print('welcome to magedu')
print('thread over')
print('current_thread again: {}'.format(threading.current_thread()))
print('current_thread().is_alive() again: {}'.format(threading.current_thread().is_alive()))
t = threading.Thread(target=worker,name='w1')
t.start()
# t1 = threading.Thread(target=worker,name='w2') #多线程无法确定谁前谁后,每个都有机会被执行到
# t1.start()
输出:
current_thread again: <_MainThread(MainThread, started 18324)>
current_thread().is_alive() again: True
current_thread: <Thread(w1, started 14036)>
current_thread().name: w1
currrent_thread().is_alive(): True
main_thread: <_MainThread(MainThread, started 18324)>
main_thread().is_alive(): True
active_count: 2
enumerate: [<Thread(w1, started 14036)>, <_MainThread(MainThread, started 18324)>]
welcome to magedu
welcome to magedu
welcome to magedu
thread over
t.name,只是个名字,是个标识,可重名,getName()获取,setName()设置;
t.ident,线程ID,是非0整数,线程启动后才会有ID,否则为None;线程退出,此ID依旧可访问,此ID可重复使用;t.name可重复,t.ident必须唯一但可在线程退出后再利用;
t.is_alive(),返回线程是否活着,True|False;
t.start(),用于多线程场景,一执行到t1.start()则开启一个新的工作线程,threading.current_thread()为<MyThread(w1, started 17760)>,主线程1个工作线程多个;
t.run(),运行线程函数,仅是普通函数调用,不会开启新的线程,在当前线程中顺序执行,threading.current_thrad()为<_MainThread(MainThread, started 15384)>,先执行t1.run()再执行t2.run();
注:多线程:
一个进程中有多个线程,实现并发;
一个进程至少有一个主线程,其它线程为工作线程;
一个进程至少有一个线程,作为程序入口,这个线程就是主线程;
多线程争用同一个资源(打饭时很多人争一个窗口),解决:加锁;
父线程不管理子线程,父、子只是为了分清谁启动的谁;
父线程消亡,子线程仍可继续运行;
主线程结束时,可杀掉工作线程(子线程);
例:
class MyThread(threading.Thread):
def run(self):
print('run')
super().run()
def start(self):
print('start')
return super().start()
def worker(n=3):
print(threading.current_thread())
for _ in range(n):
time.sleep(1)
print('welcome to magedu')
print('thread over')
t1 = MyThread(target=worker,name='w1')
t1.start()
# t1.run()
# t2 = MyThread(target=worker,name='w2')
# t2.start()
# t2.run()
输出:
start
run
<MyThread(w1, started 18324)>
welcome to magedu
welcome to magedu
welcome to magedu
thread over
多线程的执行结果,有时是意想不到的,即线程不安全,在各种高级语言中都必须面对;
如下例在pycharm中和ipython中分别执行,pycharm中整整齐齐,而ipython中不规整,ipython中运行的效果就是线程不安全;
其它语言中有:
线程安全函数;
线程不安全函数(要加锁);
线程安全类;
线程不安全类(加各种锁);
在多线程或特殊场景下应用,能不加锁就不加,否则效率极差;
例:
def worker():
for _ in range(100):
print('{} is running'.format(threading.current_thread()))
for _ in range(2):
threading.Thread(target=worker,name='worker-{}'.format('view')).start()
解决:
def worker():
for _ in range(100):
print('{} is running\n'.format(threading.current_thread()),end='')
for _ in range(2):
threading.Thread(target=worker,name='worker-{}'.format('view')).start()
与linux的daemon守护进程不一样,linux是进程级别,此处是线程;
进程靠线程执行代码,至少有一个主线程,其它线程是工作线程;
主线程是第一个启动的线程;
父线程:如果线程A启动了线程B,A是B的父线程;
子线程:B是A的子线程;
查看Thread类源码:
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None):
注:
daemon,没有主线程之说,都是当前线程;
py构造线程时,可设置daemon属性,这个属性必须在t.start()方法前设置好;
线程有daemon属性(仅py有),可显式设置为True或False;也可不设置取默认值None,如果不设置daemon就取当前线程的daemon来设置它;
主线程是non-daemon线程(铁律),即daemon=False,从主线程创建的所有线程不设置daemon属性,则默认都是daemon=False即non-daemon线程;
py程序在没有活着的non-daemon线程运行时退出(主线程kill掉所有活着的daemon线程,主线程退出),也就是剩下的只能是daemon线程,主线程才能退出,否则主线程只能等待(只有在有non-daemon线程,主线程一直等待);
daemon属性,表示线程是否是daemon线程,这个值必须在t.start()前设置,否则引发RuntimeError异常;
isDaemon(),是否是daemon线程;
setDaemon(),设置为daemon线程,必须在t.start()之前设置;
简单来说,本来并没有daemon thread,为简化程序员工作,让他们不用去记录和管理那些后台进程,创建了daemon thread概念,这个概念唯一的作用是,当你把一个线程设为daemon,它会随主线程的退出而退出;
daemon线程简化了程序员手动关闭线程的工作;
daemon线程适用场景:
1、后台任务,如:发送心跳包、监控等场景居多;
2、主线程工作才有用的线程,如主线程中维护着公共的资源,主线程已经清理了,准备退出,而工作线程使用这些资源工作也没意义了,一起退出最合适;
3、随时可被终止的线程;
如果主线程退出,想其它工作线程一起退出,就使用daemon=True来创建工作线程;
如,开启一个线程,定时判断web服务是否正常工作,主线程退出,工作线程也没必要存在了,应随主线程一起退出,这种daemon线程一旦创建,就可忘记它了,只用关心主线程什么时候退出就行;
如果在non-daemon线程(A)中,对另一个daemon线程(B)使用了join方法,这个线程B设置daemon就没意义了,因为A(non-daemon)总是要等待B的;
如果在一个daemon线程C中,对另一个daemon线程D使用了join方法,只能说明C要等待D,主线程退出,C和D不管是否结束,也不管它们谁等谁,都要被杀掉;
例:
import threading
import logging
logging.basicConfig(level=logging.INFO)
def worker():
for _ in range(100):
msg = '{} is running'.format(threading.current_thread())
logging.info(msg) #logging.info(msg)和print()的内容不规律的交替出现,原因:多线程,谁前谁后不可预知,要看cpu运行调度情况
print('inner threading.enumerate(): {}'.format(threading.enumerate()))
threading.Thread(target=worker,name='worker-{}'.format('view')).start() #工作线程未设置daemon则用主线程的daemon=False(铁律,主线程是non-daemon),主线程一直等待子线程执行完才退出
print('end')
print('main threading.enumerate(): {}'.format(threading.enumerate())) #返回主线程+其它活着的工作线程
输出:
……
INFO:root:<Thread(worker-view, started 16204)> is running
INFO:root:<Thread(worker-view, started 16204)> is running
end
inner threading.enumerate(): [<_MainThread(MainThread, started 17456)>, <Thread(worker-view, started 16204)>]
main threading.enumerate(): [<_MainThread(MainThread, started 17456)>, <Thread(worker-view, started 16204)>]
……
例:
# threading.Thread(target=worker,name='worker-{}'.format('view')).start()
threading.Thread(target=worker,name='worker-{}'.format('view'),daemon=True).start() #是daemon线程,主线程不会等待,直接杀掉daemon线程退出
print('end')
print('main threading.enumerate(): {}'.format(threading.enumerate()))
例:
logging.basicConfig(level=logging.INFO)
def worker():
threading.Thread(target=worker1,name='worker1-{}'.format('view')).start() #取当前线程的daemon,即daemon=True
for _ in range(100):
msg = '{} is running'.format(threading.current_thread())
logging.info(msg)
def worker1():
for _ in range(100):
msg = '$$$ {} is running'.format(threading.current_thread())
logging.info(msg)
# threading.Thread(target=worker,name='worker-{}'.format('view')).start()
threading.Thread(target=worker,name='worker-{}'.format('view'),daemon=True).start()
# threading.Thread(target=worker,name='worker-{}'.format('view'),daemon=False).start() #一旦该线程执行到了,则主线程一直等待;如果没有执行到(当前试验没有执行不到的情况)主线程直接退出了,如何改?加上time.sleep(0.0005)
# time.sleep(0.0005)
print('ending')
print(threading.enumerate())
t.join(timeout=None);
join()是线程的标准方法之一;
一个线程中调用另一个线程的join方法,调用者将被阻塞,直到被调用线程终止;
一个线程可被join多次;
调用谁的join方法,就是join谁,就要等谁;
timeout参数很少用,指定调用者等待多久,没有设置就一直等到被调用线程结束;
如果在non-daemon线程(A)中,对另一个daemon线程(B)使用了join方法,这个线程B设置daemon就没意义了,因为A(non-daemon)总是要等待B的;
如果在一个daemon线程C中,对另一个daemon线程D使用了join方法,只能说明C要等待D,主线程退出,C和D不管是否结束,也不管它们谁等谁,都要被杀掉;
例:
def worker():
for _ in range(50):
msg = '{} is running'.format(threading.current_thread())
logging.info(msg)
t = threading.Thread(target=worker1,name='worker1-{}'.format('view'),daemon=True)
t.start()
# t.join() #如果有此句打印行数为5050
def worker1():
for _ in range(100):
msg = '$$$ {} is running'.format(threading.current_thread())
logging.info(msg)
t = threading.Thread(target=worker,name='worker-{}'.format('view'),daemon=True)
t.start()
t.join() #可理解为join(t),当前线程是主线程,主线程和t联合起来,主线程要等t执行完才退出
例:
def bar():
while True:
time.sleep(1)
print('bar')
def foo():
print('t1 is daemon= {}'.format(threading.current_thread().isDaemon()))
t2 = threading.Thread(target=bar)
t2.start()
print('t2 is daemon= {}'.format(t2.isDaemon()))
t2.join()
t1 = threading.Thread(target=foo,daemon=True)
t1.start()
# t1.join()
time.sleep(2)
print('main thread exiting')
注:
等价于t1、t2都为daemon=False,non-daemon;
threading.local学习自java;
查看threading.local源码:
class local: #注意local首字母为小写
__slots__ = '_local__impl', '__dict__'
……
def __getattribute__(self, name): #对象所有属性均拦截,重新构建字典
class _localimpl:
def __init__(self):
# The key used in the Thread objects' attribute dicts.
# We keep it a string for speed but make it unlikely to clash with
# a "real" attribute.
self.key = '_threading_local._localimpl.' + str(id(self))
# { id(Thread) -> (ref(Thread), thread-local dict) }
self.dicts = {}
threading.local类构建了一个大字典,即{ id(Thread) -> (ref(Thread), thread-local dict) },其元素的key是线程实例的内存地址,其元素的value是二元组,二元组中的元素为(线程对象引用+每个线程的字典);
使用时:
d[key][0].name
d[key][1][key]
通过threading.local()实例就可在不同线程中,安全的使用线程独有的数据,做到了线程间数据隔离,如同本地变量一样安全;
多线程中,能不共享就不共享,用局部变量,局部变量与threading.local()比,局部变量用得多些;例如,外面传进来的参数,直接赋给本地变量,但对于引用类型来说不行(编程中经常遇到外面传来的是对象,该对象通常是类型);
例:
def worker():
x = 0
for _ in range(100):
time.sleep(0.0001)
x += 1
print(threading.current_thread(),x)
for _ in range(5):
threading.Thread(target=worker).start() #线程有自己的栈,每个线程压栈的x为函数内局部作用域(局部变量),因此多线程里看到最终x值均为100
输出:
<Thread(Thread-1, started 11216)> 100
<Thread(Thread-5, started 17792)> 100
<Thread(Thread-3, started 15924)> 100
<Thread(Thread-4, started 3248)> 100
<Thread(Thread-2, started 17804)> 100
例:
class A:
def __init__(self,x):
self.x = x
a = A(0) #解决此问题(使用全局变量,还能保持每个线程使用不同的数据)
def worker():
a.x = 0
for _ in range(100):
time.sleep(0.0001)
a.x += 1
print(threading.current_thread(),a.x)
print(a.__dict__) #每个线程有自己的字典,是隔离的
for _ in range(5):
threading.Thread(target=worker).start() #乱,不可预知,使用了全局对象,线程之间互相干扰,导致是错误的结果
输出:
<Thread(Thread-1, started 18216)> 483
<Thread(Thread-3, started 15560)> 493
<Thread(Thread-5, started 17464)> 496
<Thread(Thread-2, started 18268)> 497
<Thread(Thread-4, started 4676)> 498
例:
a = threading.local()
def worker():
a.x = 0
for _ in range(100):
time.sleep(0.0001)
a.x += 1
print(threading.current_thread(),a.x)
print(a.__dict__) #每个线程有自己的字典,是隔离的
for _ in range(5):
threading.Thread(target=worker).start() #结果与使用局部变量效果一样
输出:
<Thread(Thread-4, started 11792)> 100
<Thread(Thread-2, started 12848)> 100
<Thread(Thread-1, started 17764)> 100
<Thread(Thread-3, started 16464)> 100
<Thread(Thread-5, started 16704)> 100
例:
X = 'abc'
ctx = threading.local()
ctx.x = 123
print(ctx,type(ctx),ctx.x)
def worker():
print(X)
print(ctx)
print(ctx.x)
print('good job')
worker()
print('~'*30)
threading.Thread(target=worker).start() #ctx.x出错,AttributeError,要在worker中重新定义ctx.x=567
输出:
<_thread._local object at 0x000000000125DA40> <class '_thread._local'> 123
abc
<_thread._local object at 0x000000000125DA40>
123
good job
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
abc
<_thread._local object at 0x000000000125DA40>
Exception in thread Thread-1:
Traceback (most recent call last):
File "D:\Python\Python35\lib\threading.py", line 914, in _bootstrap_inner
self.run()
File "D:\Python\Python35\lib\threading.py", line 862, in run
self._target(*self._args, **self._kwargs)
File "E:/git_practice/cmdb/example_threading.py", line 215, in worker
print(ctx.x)
AttributeError: '_thread._local' object has no attribute 'x'
定时器,延迟执行,定义多久执行一个函数,继承自threading.Thread;
查看源码:
def __init__(self, interval, function, args=None, kwargs=None):
t.start()方法执行后,Timer()对象处于等待状态,等待interval后,开始执行function,实际是延迟执行该线程而不是函数;
non-daemon,继承Thread;
Timer是Thread的子类,就是线程类,具有线程的能力和特征;它的实例是能够延时执行目标函数的线程,在真正执行目标函数之前都可cancel它;
t.cancel()
如果在执行函数之前的等待阶段使用了t.cancel()方法,就会跳过,函数不会执行;
并不是非要在t.start()之后,只要在延迟时间内取消都可以;
查看源码,通过event实现t.cancel();
例:
# def add():
# print(4 + 5)
# print(threading.enumerate())
# t = threading.Timer(3,add)
# t.setName('w1')
# t.start()
def add(x,y):
print(x + y)
print(threading.enumerate())
t1 = threading.Timer(2,add,args=(4,4))
t1.setName('w2')
t1.start()
# t1.cancel() #此句可不在t.start()之后,只要在延迟时间内取消都可以;如果线程中add函数已经开始执行了,cancel就没任何效果了
print(threading.enumerate())
time.sleep(3)
t1.cancel() #此时取消无效,3>2,add函数已经执行
输出:
[<_MainThread(MainThread, started 14560)>, <Timer(w2, started 17696)>]
8
[<_MainThread(MainThread, started 14560)>, <Timer(w2, started 17696)>]
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。