tcp传输:

  传输需要ack回应,然后才清空缓存,服务端先起来。

  tcp流式协议,tcp的Nagle的优化算法,会将时间间隔短,数据量小的打包成一个,然后发送给对方,减少发送的次数。

UDP协议:

  不用建连接,不用确认。速度快,强调速度的场景,ntp,dns,udp有效的传输512bytes。

  不用listen,accept,connect.

  udp套接字,如果一次没有收够的话,剩下的丢弃,在linux中,比如 recvfrom(3)    sendto(4) 就会只接收三个。windows系统会报错。

from  socket import *

server = socket(AF_INET,SOCK_DGRAM)
server.bind(("127.0.0.1",8081)) while True:
data,client_addr = server.recvfrom(1024)
print(data,client_addr)
server.sendto(data.upper(),client_addr例子

例子,服务端

from  socket import *

client = socket(AF_INET,SOCK_DGRAM)

while True:
msg = input(">>>:").strip()
client.sendto(msg.encode('utf-8'),("127.0.0.1",8081))
data,server_addr = client.recvfrom(1024) print(data.decode('utf-8'))

客户端

操作系统的概念:

  操作的作用:操作硬件,封装接口,控制进程,控制竞争。

  进程:相当于部门

  线程:是部门中干活的人

  多道技术:当cpu在等待i/o的过程中,去干其他的工作。背景:单核下实现并发的效果。

    (1)空间上的复用:内存分为及部分,每个部分放入一个程序

    (2)时间上的复用:当一个进程在等待i/o时候,另一个程序可以使用cpu,当一个程序在等待I/O时,另一个程序可以使用cpu,  如果内存中可以同时存放足够多的作业,则cpu的利用率可以接近100%,类似于我们小学数学所学的统筹方法。(操作系统采用了  多道技术后,可以控制进程的切换,或者说进程之间去争抢cpu的执行权限。这种切换不仅会在一个进程遇到io时进行,一个进程占  用。

  (3)切换的3种情况

    cpu时间过长也会切换,或者说被操作系统夺走cpu的执行权限.

    有i/o切换时,提升效率。

    优先级切

并发与并行:  

  无论是并行还是并发,在用户看来都是'同时'运行的,不管是进程还是线程,都只是一个任务而已,真的干活的是cpu,cpu来做这些任务,而一个cpu同一时刻只能执行一个任务

一 并发:是伪并行,即看起来是同时运行。单个cpu+多道技术就可以实现并发,(并行也属于并发)

  二 并行:同时运行,只有具备多个cpu才能实现并行

单核下,可以利用多道技术,多个核,每个核也都可以利用多道技术(多道技术是针对单核而言的

  

进程的三种状态:

        

    

  在linux中,进程创建调用fork

  在windows中,进程创建调用 CreateProcess

关于创建的子进程,UNIX和windows

  1.相同的是:进程创建后,父进程和子进程有各自不同的地址空间(多道技术要求物理层面实现进程之间内存的隔离),任何一个进程的在其地址空间中的修改都不会影响到另外一个进程。

  2.不同的是:在UNIX中,子进程的初始地址空间是父进程的一个副本,提示:子进程和父进程是可以有只读的共享内存区的。但是对于windows系统来说,从一开始父进程与子进程的地址空间就是不同的。

开进程的两种方式:

  #windows开进程的话,进程必须放在主函数下面。

  方法一

from  multiprocessing import Process
import time
def task(name):
print("%s is running" %name)
time.sleep(3)
print("%s is down" %name) if __name__ == '__main__':
p = Process(target=task,args=('zym'))
p.start()
print("主") ##这里创建进程需要绑定内存空间可能需要时间。

  方法二

from  multiprocessing import Process
import time class MyProcess(Process):
def __init__(self,name): #定义自己的属性会把父类覆盖掉
super(MyProcess,self).__init__() #重用父类
self.name =name def run(self):
print("%s is running" % self.name)
time.sleep(3)
print("%s is down" % self.name) if __name__ == '__main__':
p =MyProcess('进程一')
p.start() # 就是p.run() 因为是自己定义的进程,所以要自己写run函数

进程的内存空间:

  就像各个部门之间的工作内容,互不干涉。

进程中的函数:

  join    #例如:p.join()  p为实例化的一个进程对象,join的意思是执行完这个进程在执行下面的代码,

     场景,让主进程等待子进程完成在行动。

僵尸进程和孤儿进程:

参考博客:http://www.cnblogs.com/Anker/p/3271773.html

一:僵尸进程(有害)
  僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。详解如下 我们知道在unix/linux中,正常情况下子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束,如果子进程一结束就立刻回收其全部资源,那么在父进程内将无法获取子进程的状态信息。 因此,UNⅨ提供了一种机制可以保证父进程可以在任意时刻获取子进程结束时的状态信息:
1、在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)
2、直到父进程通过wait / waitpid来取时才释放. 但这样就导致了问题,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。   任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。 二:孤儿进程(无害)   孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。   孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。 我们来测试一下(创建完子进程后,主进程所在的这个脚本就退出了,当父进程先于子进程结束时,子进程会被init收养,成为孤儿进程,而非僵尸进程),文件内容 import os
import sys
import time pid = os.getpid()
ppid = os.getppid()
print 'im father', 'pid', pid, 'ppid', ppid
pid = os.fork()
#执行pid=os.fork()则会生成一个子进程
#返回值pid有两种值:
# 如果返回的pid值为0,表示在子进程当中
# 如果返回的pid值>0,表示在父进程当中
if pid > 0:
print 'father died..'
sys.exit(0) # 保证主线程退出完毕
time.sleep(1)
print 'im child', os.getpid(), os.getppid() 执行文件,输出结果:
im father pid 32515 ppid 32015
father died..
im child 32516 1 看,子进程已经被pid为1的init进程接收了,所以僵尸进程在这种情况下是不存在的,存在只有孤儿进程而已,孤儿进程声明周期结束自然会被init来销毁。 三:僵尸进程危害场景:   例如有个进程,它定期的产 生一个子进程,这个子进程需要做的事情很少,做完它该做的事情之后就退出了,因此这个子进程的生命周期很短,但是,父进程只管生成新的子进程,至于子进程 退出之后的事情,则一概不闻不问,这样,系统运行上一段时间之后,系统中就会存在很多的僵死进程,倘若用ps命令查看的话,就会看到很多状态为Z的进程。 严格地来说,僵死进程并不是问题的根源,罪魁祸首是产生出大量僵死进程的那个父进程。因此,当我们寻求如何消灭系统中大量的僵死进程时,答案就是把产生大 量僵死进程的那个元凶枪毙掉(也就是通过kill发送SIGTERM或者SIGKILL信号啦)。枪毙了元凶进程之后,它产生的僵死进程就变成了孤儿进 程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源,这样,这些已经僵死的孤儿进程 就能瞑目而去了。 四:测试
#1、产生僵尸进程的程序test.py内容如下 #coding:utf-8
from multiprocessing import Process
import time,os def run():
print('子',os.getpid()) if __name__ == '__main__':
p=Process(target=run)
p.start() print('主',os.getpid())
time.sleep(1000) #2、在unix或linux系统上执行
[root@vm172-31-0-19 ~]# python3 test.py &
[1] 18652
[root@vm172-31-0-19 ~]# 主 18652
子 18653 [root@vm172-31-0-19 ~]# ps aux |grep Z
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 18653 0.0 0.0 0 0 pts/0 Z 20:02 0:00 [python3] <defunct> #出现僵尸进程
root 18656 0.0 0.0 112648 952 pts/0 S+ 20:02 0:00 grep --color=auto Z [root@vm172-31-0-19 ~]# top #执行top命令发现1zombie
top - 20:03:42 up 31 min, 3 users, load average: 0.01, 0.06, 0.12
Tasks: 93 total, 2 running, 90 sleeping, 0 stopped, 1 zombie
%Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1016884 total, 97184 free, 70848 used, 848852 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 782540 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
root 20 0 29788 1256 988 S 0.3 0.1 0:01.50 elfin #3、
等待父进程正常结束后会调用wait/waitpid去回收僵尸进程
但如果父进程是一个死循环,永远不会结束,那么该僵尸进程就会一直存在,僵尸进程过多,就是有害的
解决方法一:杀死父进程
解决方法二:对开启的子进程应该记得使用join,join会回收僵尸进程
参考python2源码注释
class Process(object):
def join(self, timeout=None):
'''
Wait until child process terminates
'''
assert self._parent_pid == os.getpid(), 'can only join a child process'
assert self._popen is not None, 'can only join a started process'
res = self._popen.wait(timeout)
if res is not None:
_current_process._children.discard(self) join方法中调用了wait,告诉系统释放僵尸进程。discard为从自己的children中剔除 解决方法三:http://blog.csdn.net/u010571844/article/details/50419798

僵尸进程和孤儿进程解释

守护进程:

  当你的子进程伴随主进程的全部生命周期,主进程死了(主进程代码运行完毕),子进程没必要存在了,这时可以设置为守护进程。

  在p.start()之前加上p.daemon = True,p即为守护进程     !!!但是守护进程中无法在开子进程。

互斥锁:

from multiprocessing import Process,Lock
import json
import random
import time
def look():
time.sleep(random.randint(1,3)) #模拟网络延迟
dic = json.load(open("db.txt",'r',encoding='utf-8')) #json 文件内容 {"count": 0}
print("剩下的票数是 %s" %dic["count"]) def buy(lock):
lock.acquire()
dic = json.load(open("db.txt", 'r', encoding='utf-8'))
if dic['count'] >= 1:
dic['count'] -=1
time.sleep(random.randint(1, 3))
json.dump(dic,open("db.txt", 'w', encoding='utf-8'))
print("购票成功")
else:print("购票失败")
lock.release()
def task(lock):
look()
buy(lock)
if __name__ == '__main__':
lock = Lock()
for p in range(4): #用for循环开启,
p=Process(target=task,args=(lock,))
p.start()
#目的查票同时进行,改票只能一个用户进行。
#互斥锁只能acquire一次,再次使用的话只能现释放在使用。
#使用时当做参数传进去。
# 互斥锁的使用,可以在函数里面用,需要的代码加锁,也可以对整个函数使用。

互斥锁

递归锁:

#死锁现象,就是两个线程之间互相把对方锁在外边了。
#递归锁,创建一个递归锁,之后每使用这把锁,这个锁的计数就会加一,可以连续acquire,只要递归锁的计数不为0,其他人都抢不到这把锁。
#只有进程将这个锁的计数全部释放,这把锁才能再次被线程争抢(包括他自己)
from threading import Thread,RLock
(Lock为正常锁,Rlock为递归锁用法一致)
mutexA = Lock()#创建锁
mutexA = Rlock()#创建递归锁
mutexA.acpuire()#加锁
mutexA.release()#释放

信号量:

  另一种形式的锁,例如设置信号量为5,即有5个钥匙,拿到钥匙才能进门,出门时再将钥匙放在门前,由其他人去拿。

  

from  threading  import Thread,Semaphore,current_thread
sm = Semaphore(5) #设置信号量为5
import time
import random
def task():
with sm:
print("%s is laing " %current_thread().getName())#线程的名字
time.sleep(random.randint(1,3)) #模拟网络延迟 if __name__ == '__main__':
for i in range(20): #起20个线程
t = Thread(target=task,)
t.start()

信号量

Event事件:

  同进程的一样

  线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常  棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信  号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设  置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行

event.isSet():返回event的状态值;

event.wait():如果 event.isSet()==False将阻塞线程;

event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;

event.clear():恢复event的状态值为False。
from threading import Thread,Event,current_thread
import time
def mysql_check():
print('\033[45m[%s]正在检查mysql\033[0m' % current_thread().getName())
time.sleep(5)
event.set() def mysql_connect():
count = 1
while not event.is_set():
if count > 3 :
raise TimeoutError('连接超时')
event.wait(2) #wait后面的2 * count的值大于sleep中的5才能接收到event设置的值
print("%s 尝试重新链接 ,time %s"%(current_thread().getName(),count))
count += 1
print("succes") if __name__ == '__main__':
event =Event() #实例化
t1 = Thread(target=mysql_check,)
t2 = Thread(target=mysql_connect,)
t3 = Thread(target=mysql_connect,)
t4 = Thread(target=mysql_connect,) t1.start()
t2.start()
t3.start()
t4.start()

event

定时器:

指定多长时间后执行

from threading import Timer

def fanc(name):
print("%s is aaaaa" % name) t = Timer(3,fanc,args=('zym',)) #args后面是参数,需要加括号(就是一个元组,传参数时注意顺序)
t.start() #如果在固定点运行需要在前端的定时器,这个实现不了

线程之间的通信:

  推荐使用队列,因为可以不用考虑锁的问题。

  进程队列,线程队列(耦合度高,性能问题,损坏的问题) 所以一般用单独的一台或者集群来使用队列软件,例如rabbitmq集群

  

import queue

q=queue.Queue(3) #队列:先进先出

q.put(1)
q.put(2)
q.put(3)
q.get() #加上这个 ,q.put(4,block=True,timeout=3)将不会报错
# q.put(4)
# q.put_nowait(4)
# q.put(4,block=False)
q.put(4,block=True,timeout=3) #先取出一个,然后再放入,否则会报错 # print(q.get())
# print(q.get())
# print(q.get()) q=queue.LifoQueue(3) #堆栈:后进先出
q.put(1)
q.put(2)
q.put(3) print(q.get())
print(q.get())
print(q.get()) q=queue.PriorityQueue(3) #优先级队列,vip类型,传值类表或元组,数字越小,优先级越高
q.put((10,'a'))
q.put((-3,'b'))
q.put((100,'c')) print(q.get())
print(q.get())
print(q.get())

进程池:

  

#进程池
#提交任务的两种方式:
#同步调用:提交完任务后就在原地等待,拿到任务的返回值,才能继续下一行代码,导致程序串行执行
#异步调用:提交完任务后,不在原地等待,结果?程序是并发执行的,一般与回调机制一块使用 #进程的执行状态:
#阻塞
#非阻塞 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import os
import time
import random
def task(n):
print('%s is runing ' %os.getpid() )
time.sleep(random.randint(1,3))
return n**2 def handle(res):
res = res.result()
print('handle res %s' %res) if __name__ == '__main__':
pool = ProcessPoolExecutor(4) #不指定,默认为cpu的盒数,一般超过cpu盒数的两倍,从始到总就是这4个进程在干活。 for i in range(13):
obj = pool.submit(task,i) #提交的进程池,i即传入的参数,submit是异步
obj.add_done_callback(handle) #回调机制,触发handle,回调的是个对象,然后使用 result()取出结果 pool.shutdown(wait=True) #相当于join,(关门,不能再向池子里丢任务了),wait =TRUE,就是在进程池里的任务全部跑完,挨个取出,执行join操作(默认即为True)。
print('主')

线程池:

  与进程池用法一致。默认池子线程是核心数的5倍。

  区别包的名字有所不同

  pool = ThreadPoolExecutor()

协程:

  协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。、

    需要强调的是:

  #1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
  #2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)

  对比操作系统控制线程的切换,用户在单线程内控制协程的切换

  优点如下:

  #1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
  #2. 单线程内就可以实现并发的效果,最大限度地利用cpu

  缺点如下:

  #1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
  #2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

  比如nginx就是利用协程的方法实现轻量级的并发。

协程的使用:(遇到io在进行切换)

使用一:  

from gevent import monkey;monkey.patch_all()  ##打一个补丁,让gevent模块识别其他的io操,将阻塞变为非阻塞,让gevent来帮助切换
import gevent
import time def eat(name):
print('%s eat 1' %name)
# gevent.sleep(3)
time.sleep(3)
print('%s eat 2' %name) def play(name):
print('%s play 1' % name)
# gevent.sleep(2)
time.sleep(3)
print('%s play 2' % name) g1=gevent.spawn(eat,'egon') #提交任务异步
g2=gevent.spawn(play,'alex')
# gevent.sleep(1) # g1.join()
# g2.join()
gevent.joinall([g1,g2]) #让线程不要死掉否则看不到结果

gevent

使用二:

  利用套接字来实现,server端采用一个线程,使用gevent模块。使用协程,因为在套接字建立连接,收发消息的时候会有阻塞的情况。所以可以采用协程的方式。

  

import socket
from gevent import monkey,spawn;monkey.patch_all() #打补丁,识别其他的io操作 def accept():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', 8083))
server.listen(5)
while True:
conn,addr = server.accept()
# print('from addr is %s' %str(addr)) spawn(send,conn) #提交任务 def send(conn):
while True:
data = conn.recv(1024)
conn.send(data.upper()) if __name__ == '__main__':
g = spawn(accept,) #实例化一个对象
g.join() #防止进程死亡

server端

import socket
from threading import Thread,current_thread,Thread
def client():
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1', 8083)) while True:
client.send(('%s is in connecting' % current_thread().getName()).encode('utf-8'))
data = client.recv(1024)
print(data.decode('utf-8'))
client.close()
if __name__ == '__main__':
for i in range(10):
t = Thread(target=client)
t.start()

client

  

  

python 阿狸的进阶之路(9)的更多相关文章

  1. python 阿狸的进阶之路(6)

    常用模块 json # 序列化 #将内存的数据存到硬盘中,中间的格式,可以被多种语言识别,跨平台交互数据 #json 可以将字典之类的数据类型存到字典中 import json dic = {&quo ...

  2. python 阿狸的进阶之路(5)

    一.模块 1.什么是模块: 包含了一组功能的python文件,文件名是xxx.py,模块名是module. 可以使用 import module,四个通用的类别: (1)用python编写的py文件 ...

  3. day3 python 阿狸的进阶之路

    函数概念: 1.为什要有函数 组织结构不清晰,可读性差,代码冗余,可扩展性差. 2.什么是函数   具备某一个功能的工具--->函数     事先准备工具->函数的定义     拿来就用. ...

  4. python 阿狸的进阶之路(8)

    异常处理 http://www.cnblogs.com/linhaifeng/articles/6232220.html(转) 网络编程socket http://www.cnblogs.com/li ...

  5. python 阿狸的进阶之路(7)

    面向对象 转自林海峰的博客  http://www.cnblogs.com/linhaifeng/articles/6182264.html 面向对象的理解: 将数据分类,比如学生类.数据有关的函数, ...

  6. python 阿狸的进阶之路(4)

    装饰器 #1.开放封闭原则:对扩展开放,对修改是封闭#2.装饰器:装饰它人的,器指的是任意可调用对象,现在的场景装饰器->函数,被装饰的对象也是->函数#原则:1.不修改被装饰对象的源代码 ...

  7. Python 从入门到进阶之路(一)

    人生苦短,我用 Python. Python 无疑是目前最火的语言之一,在这里就不再夸他的 NB 之处了,本着对计算机编程的浓厚兴趣,便开始了对 Python 的自学之路,并记录下此学习记录的心酸历程 ...

  8. Python 从入门到进阶之路(七)

    之前的文章我们简单介绍了一下 Python 中异常处理,本篇文章我们来看一下 Python 中 is 和 == 的区别及深拷贝和浅拷贝. 我们先来看一下在 Python 中的双等号 == . == 是 ...

  9. Python 从入门到进阶之路(六)

    之前的文章我们简单介绍了一下 Python 的面向对象,本篇文章我们来看一下 Python 中异常处理. 我们在写程序时,有可能会出现程序报错,但是我们想绕过这个错误执行操作.即使我们的程序写的没问题 ...

随机推荐

  1. 外观(Facade)模式

    外观模式:为子系统中的一组接口提供一个一致的界面.此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用 在软件开发中,有时候为了完成一项较为复杂的功能,一个客户类需要和多个业务类交互,而这些需 ...

  2. flume-sink

    概述 从Flume Agent移除数据并写入到另一个Agent或数据存储或一些其他存储系统的组件被称为sink.Sink不断的轮询channel中的事件且批量的移除它们.这些事件批量写入到存储或索引系 ...

  3. Eclipse使用Maven创建Web时错误:Could not resolve archetype org.apache.maven.archetypes:maven-archetype-webap

    网上也有好多方法我没有试成功,不过我将maven的conf setting.xml里的 阿里镜像给注释就可以了,你们也可以试试

  4. [UE4]UMG和关卡坐标变换、旋转小地图

    一.优化上一节的蓝图,新建一个函数addFlagToCanvas(动态添加图标到Canvas) 二. 分析地图坐标系和UMG坐标系 要根据实际情况分析关卡坐标系. UserWidget中的坐标系 三. ...

  5. [UE4]Format Text

    蓝图会自动把字符串中的占位换成参数输入. 字符串不会自动转换,需要手动转换

  6. nginx file not found 错误处理小记

    安装完php php-fpm nginx 后访问php出现file not found错误,html就没问题配置文件server 段如下 server { listen 80; server_name ...

  7. Centos 6.5(64位) vim 8.0 安装

    转自:https://blog.csdn.net/sdoyuxuan/article/details/78825912 1 先得安装nurses库 yum list | grep "ncur ...

  8. [UE4][Canvas]用C++代码绘制血条(HealthBar)

    转自:http://aigo.iteye.com/blog/2275110 参考自Epic官方项目StrategyGame 血条效果: StrategyHUD.h StrategyHUD.cpp

  9. vue2.0-组件传值

    父组件给子组件传值,子组件用props接收 例子:两个组件,一个是父组件标签名是parent,一个是子组件标签名是child,并且child组件嵌套在父组件parent里,大概的需求是:我们子组件里需 ...

  10. Chapter1:Qt概念

    信号和槽1.信号与槽机制的连接方式(1):一个信号可以与另一个信号相连,代码如下: connect(Object1,SIGNAL(signal1),Object2,SIGNAL(signal1)); ...